JAVA之Java线程核心详解

news2025/3/13 13:18:25

Java线程核心

1.进程和线程

进程:进程的本质是一个正在执行的程序,程序运行时系统会创建一个进程,并且给每个进程分配独立的内存地址空间保证每个进程地址不会相互干扰。同时,在 CPU 对进程做时间片的切换时,保证进程切换过程中仍然要从进程切换之前运行的位置出开始执行。所以进程通常还会包括程序计数器、堆栈指针。

线程:有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派 CPU 的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

2.线程的创建方式

创建的方式有三种:

  1. 继承Thread
  2. 实现Runnable接口
  3. Callable/Future

import java.util.concurrent.CountDownLatch;
 
public class ThreadDemo1 extends Thread {
    CountDownLatch countDownLatch;
 
    public ThreadDemo1(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
 
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + ":my thread ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            countDownLatch.countDown();
        }
    }
 
    public static void main(String[] args) {
        // 第一种:使用extends Thread方式
        CountDownLatch countDownLatch1 = new CountDownLatch(2);
        for (int i = 0; i < 2; i++) {
            ThreadDemo1 myThread1 = new ThreadDemo1(countDownLatch1);
            myThread1.start();
        }
 
        try {
            countDownLatch1.await();
            System.out.println("thread complete...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

 
}

 
import java.util.concurrent.CountDownLatch;
 
 
public class ThreadDemo2  implements Runnable{
    CountDownLatch countDownLatch;
 
    public ThreadDemo2(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + ":my runnable ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            countDownLatch.countDown();
        }
    }
 
    public static void main(String[] args) {
        // 第二种:使用implements Runnable方式
        CountDownLatch countDownLatch2 = new CountDownLatch(2);
        ThreadDemo2 myRunnable = new ThreadDemo2(countDownLatch2);
        for (int i = 0; i < 2; i++) {
            new Thread(myRunnable).start();
        }
 
        try {
            countDownLatch2.await();
            System.out.println("runnable complete...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 
public class ThreadDemo3 implements Callable<Integer> {
 
    public static void main(String[] args) {
        ThreadDemo3 threadDemo03 = new ThreadDemo3();
        //1、用futureTask接收结果
        FutureTask<Integer> futureTask = new FutureTask<>(threadDemo03);
        new Thread(futureTask).start();
 
        //2、接收线程运算后的结果
        try {
            //futureTask.get();这个是堵塞性的等待
            Integer sum = futureTask.get();
            System.out.println("sum="+sum);
            System.out.println("-------------------");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <101 ; i++) {
            sum+=i;
        }
        return sum;
    }
}


3.线程的生命周期

  • 新建状态(New):线程对象被创建后,但还没有调用start()方法时的状态。
  • 就绪状态(Runnable):线程对象调用start()方法后进入就绪状态,表示线程可以被调度执行。
  • 运行状态(Running):线程被调度执行后进入运行状态。
  • 等待状态(WAITING): 线程需要等待其他线程做出一些特定动作(通知或中断)
  • 阻塞状态(Blocked):线程在执行过程中可能因为某些原因被阻塞,例如等待输入输出、线程休眠等。
  • 结束状态(Terminated):线程执行完任务后进入结束状态。

在这里插入图片描述

4.线程的启动和终止

4.1 启动线程

  启动线程是start方法,非run方法

4.2 线程的终止

使用退出标志退出线程

一般 run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。它们需要长时间的运行,只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环

例如:最直接的方法就是设一个boolean 类型的标志,并通过设置这个标志为 true或 false 来控制 while循环是否退出。定义了一个退出标志 exit,当 exit 为 true 时,while 循环退出,exit 的默认值为 false.在定义 exit时,使用了一个 Java 关键字 volatile,这个关键字的目的是使 exit 同步,也就是说在同一时刻只能由一个线程来修改 exit 的值。

class FlagThread extends Thread {
 // 自定义中断标识符
 public volatile boolean isInterrupt = false;
 @Override
 public void run() {
     // 如果为 true -> 中断执行
     while (!isInterrupt) {
         System.out.println("业务处理1....");
         // 业务逻辑处理
         Thread.sleep(10000);
         System.out.println("业务处理2....");
     }
 }
}

Interrupt方法结束线

线程处于阻塞状态:如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时,会使线程处于阻塞状态。当调用线程的 interrupt()方法时,会抛出 InterruptException 异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让我们有机会结束这个线程的执行。通常很多人认为只要调用 interrupt 方法线程就会结束,实际上是错的, 一定要先捕获 InterruptedException 异常之后通过 break 来跳出循环,才能正常结束 run 方法。

public static void main(String[] args) throws InterruptedException {
// 创建可中断的线程实例
Thread thread = new Thread(() -> {
  while (!Thread.currentThread().isInterrupted()) {
      System.out.println("thread 执行步骤1:线程即将进入休眠状态");
      try {
          // 休眠 1s
          Thread.sleep(1000);
      } catch (InterruptedException e) {
          System.out.println("thread 线程接收到中断指令,执行中断操作");
          // 中断当前线程的任务执行
          break;
      }
      System.out.println("thread 执行步骤2:线程执行了任务");
  }
});
thread.start(); // 启动线程

// 休眠 100ms,等待 thread 线程运行起来
Thread.sleep(100);
System.out.println("主线程:试图终止线程 thread");
// 修改中断标识符,中断线程
thread.interrupt(); // 首先会改变当前线程的阻塞状态 true 。同时如何线程调用 sleep wait 这些阻塞的方法。那么会抛出 InterruptedException 异常
}

线程未处于阻塞状态:使用 isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置 true,和使用自定义的标志来控制循环是一样的道理。

stop 方法终止线程

程序中可以直接使用 thread.stop()来强行终止线程,但是 stop 方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出 ThreadDeatherror 的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用 stop 方法来终止线程。

5.线程间通信

  1. wait()和notify()方法:wait()方法使线程进入等待状态,直到其他线程调用notify()或notifyAll()方法将其唤醒。notify()方法唤醒一个等待中的线程,notifyAll()方法唤醒所有等待中的线程。
  • wait(long timeout)和notify()方法:wait(long timeout)方法使线程进入等待状态,直到其他线程调用notify()方法将其唤醒,或者等待时间超过指定的timeout时间。notify()方法唤醒一个等待中的线程。
  • join()方法:join()方法使一个线程等待另一个线程执行完毕。当一个线程调用另一个线程的join()方法时,当前线程将被阻塞,直到另一个线程执行完毕。
  • Lock和Condition接口:Lock接口提供了比synchronized关键字更灵活的锁机制,Condition接口提供了更灵活的等待/通知机制。通过Lock接口的lock()方法获取锁,unlock()方法释放锁;通过Condition接口的await()方法使线程等待,signal()方法唤醒一个等待中的线程,signalAll()方法唤醒所有等待中的线程。
  • BlockingQueue阻塞队列:BlockingQueue是一个支持阻塞操作的队列,当队列为空时,获取元素的线程将被阻塞,直到队列中有可用元素;当队列满时,插入元素的线程将被阻塞,直到队列有空闲位置。

6.线程池

6.1 线程池原理

提交一个任务到线程池中,线程池的处理流程如下:

  1. 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
  2. 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程
  3. 判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

线程池的好处:

  • 降低资源消耗:通过重复利用已创建的线程降低线程的创建和销毁造成的不必要资源消耗
  • 提高响应速度:当任务到达时,任务可以不需要等到线程创建就立即开始执行
  • 提高线程的可管理性:统一分配、监控、调优

6.2 常见的线程池实现

线程池说明
newCachedThreadPool创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行
很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造
的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并
从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资
源。
newFixedThreadPool创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大
多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,
则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何
线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之
前,池中的线程将一直存在。
newScheduledThreadPool创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
newSingleThreadExecutorExecutors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程
池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

6.3 ThreadPoolExecutor

  系统提供的线程池的实现都有各自的优缺点。我们在实际的使用中更多的是自定义线程池的实现

public ThreadPoolExecutor(int corePoolSize,
                               int maximumPoolSize,
                               long keepAliveTime,
                               TimeUnit unit,
                               BlockingQueue<Runnable> workQueue,
                               RejectedExecutionHandler handler) 


各个参数的含义:

corePoolSize:线程池核心线程数量
maximumPoolSize:线程池最大线程数量
keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
unit:存活时间的单位
workQueue:存放任务的队列
handler:超出线程范围和队列容量的任务的处理程序

使用的是阻塞队列:

  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • SynchronousQueue
  • PriorityBlockingQueue

拒绝策略分类:

  • AbortPolicy:直接抛出异常,默认策略
  • CallerRunsPolicy:用调用者所在的线程来执行任务
  • DiscardOldestPolicy:丢弃阻塞对类中靠最前的任务,并执行当前任务
  • DiscardPolicy:直接丢弃任务

提交一个任务到线程池中,线程池的处理流程如下:

  1. 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
  2. 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
  3. 判断线程池里的线程是否达到最大线程数,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

在这里插入图片描述

6.4 线程池调优

要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析。

  • 任务的性质:CPU密集型任务、IO密集型任务和混合型任务

  • 任务的优先级:高中低

  • 任务的执行时间:长中短

  • 任务 的依赖性;是否依赖其他系统资源

  • CPU密集型任务应配置尽可能小的线程,如配置NCPU+1个线程的线程池,

  • IO密集型任务线程并不是一直在执行任务 ,则应配置尽可能多的线程,如2*NCPU 。

可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数
优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理,它可以让优先级高的任务先执行。

7.Synchronized

  同步、重量级锁,synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入临界区,同时还可以保证共享变量的内存可见性(单台JVM内)

synchronized锁对象:

  • 普通同步方法,锁的是当前实例对象
  • 静态同步方法,锁的是当前类的class对象
  • 同步代码块,锁的是括号里的对象(实例对象或者class对象)

synchronized的锁优化

  • 无锁
  • 偏向锁:为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,主要尽可能避免不必须要的CAS操作,如果竞争锁失败,则升级为轻量级锁
  • 轻量级锁:自旋方式,该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁(循环方式)
  • 重量级锁:阻塞方式
  • 锁消除: 有些代码明明不用加锁,结果还给加上锁,这时编译器会判断锁没有什么必要,就直接把锁去掉了
  • 锁粗化:将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。例如for循环内部获取锁

8.AQS

AbstractQueuedSynchronizer,同步器,实现JUC核心基础组件,解决了子类实现同步器时涉及到的大量细节性问题,例如:同步状态、FIFO同步队列等,采用模板方法模式,AQS实现了大量的通用方法,子类通过继承方式实现其抽象方法来管理同步状态

同步锁获取与释放:

  • 独占式
  • 共享式
package com.boge.flow.aqs;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AQSDemo {

    // 共享的资源
    private static  int count = 0;

    private static Lock lock = new ReentrantLock();

    /**
     * 造成数据安全问题的原因
     *   1.原子性:执行的最小单元  要么都执行。要么都不执行
     *   2.可见性:
     *   3.有序性:
     */
    // 定义一个方法去操作资源
    public static  void incr(){
        try {
            Thread.sleep(10);
            lock.lock(); // 加锁
            // ...
            lock.lock();
            count ++; // 原子  可见性
        }catch (Exception e){

        }finally {
            lock.unlock();
            lock.unlock(); // 释放锁
        }
    }

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(100);
        // 多个线程去操作共享资源
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    incr();
                    latch.countDown();
                }
            }).start();
        }
        latch.await(); // 阻塞
        // 获取 共享的数据
        System.out.println("count = " + count);
    }
}

在这里插入图片描述

在这里插入图片描述

9.ThreadLocal

  一种解决多线程环境下成员变量的问题的方案,但是与线程同步无关,其思路就是为每个线程创建一个单独的变量副本。从而每个线程都可以独立的改变自己所拥有的变量副本,而不会影响其他线程对应的变量副本 , ThreadLocal不是用于解决共享变量的问题,也不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制

  ThreadLocal可以理解为线程本地变量,他会在每个线程都创建一个副本,那么在线程之间访问内部副本变量就行了,做到了线程之间互相隔离,相比于synchronized的做法是用空间来换时间。

  ThreadLocal有一个静态内部类ThreadLocalMap,ThreadLocalMap又包含了一个Entry数组,Entry本身是一个弱引用,他的key是指向ThreadLocal的弱引用,Entry具备了保存key value键值对的能力。

  弱引用的目的是为了防止内存泄露,如果是强引用那么ThreadLocal对象除非线程结束否则始终无法被回收,弱引用则会在下一次GC的时候被回收。

  但是这样还是会存在内存泄露的问题,假如key和ThreadLocal对象被回收之后,entry中就存在key为null,但是value有值的entry对象,但是永远没办法被访问到,同样除非线程结束运行。

  但是只要ThreadLocal使用恰当,在使用完之后调用remove方法删除Entry对象,实际上是不会出现这个问题的。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1453681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 抽象容器类源码剖析

总体介绍 抽象容器类接口和具体容器类的关系如图所示&#xff0c;顶层包括Collection、List、Set、Queue、Deque和Map6个抽象容器类。 AbstractCollection&#xff1a;实现了Collection接口&#xff0c;被抽象类AbstractList、AbstractSet、AbstractQueue继承&#xff0c;Arra…

飞天使-k8s知识点19-kubernetes实操4-资源调度 标签和选择器:Label与Selector的使用-版本回退

文章目录 添加label资源调度 Deployment&#xff1a;创建与配置文件解析rs pod deploy 的关联信息展示Deployment&#xff1a;滚动更新Deployment&#xff1a;回滚回退版本实际操作 添加label [rootkubeadm-master1 test]# kubectl get pod NAME …

OpenCV Mat实例详解 四

OpenCV Mat实例详解三中详细介绍来了OpenCV Mat类的公有静态成员函数&#xff0c;下面介绍OpenCV Mat类的其他常用成员函数。 OpenCV Mat类常用成员函数 Mat & adjustROI (int dtop, int dbottom, int dleft, int dright)&#xff1b; dtop ROI 上边界移动值&#xff0c;如…

Java实现新能源电池回收系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户档案模块2.2 电池品类模块2.3 回收机构模块2.4 电池订单模块2.5 客服咨询模块 三、系统设计3.1 用例设计3.2 业务流程设计3.3 E-R 图设计 四、系统展示五、核心代码5.1 增改电池类型5.2 查询电池品类5.3 查询电池回…

LabVIEW智能监测系统

LabVIEW智能监测系统 设计与实现一个基于LabVIEW的智能监测系统&#xff0c;通过高效的数据采集和处理能力&#xff0c;提高监测精度和响应速度。系统通过集成传感器技术与虚拟仪器软件&#xff0c;实现对环境参数的实时监测与分析&#xff0c;进而优化监控过程&#xff0c;提…

一些配置问题记录

真的很感慨 为什么一开始的下载的软件还能用 卸载或重装后的软件总是存在各种各样的错误 真令人心烦 GNURADIO运行简单的采集信号程序报错&#xff0c; 其实不太理解为什么会出现这类错误&#xff0c;解决方法为 安装 jackd2 软件包&#xff0c;然后尝试手动启动 Jack 服务器…

通过玩游戏学会AWS

游戏名字&#xff1a; Cloud Quest 类型&#xff1a;亚马逊云科技官方出了一款 3D 角色扮演、虚拟城市建造形式的游戏实验课 进入方法&#xff1a;浏览器搜索 Cloud Quest&#xff08;或扫描下方二维码&#xff09;进入 Cloud Quest 课程页。 选择以下的链接 点击进行注册 进…

力扣题目训练(12)

2024年2月5日力扣题目训练 2024年2月5日力扣题目训练476. 数字的补数482. 密钥格式化485. 最大连续 1 的个数148. 排序链表164. 最大间距 2024年2月5日力扣题目训练 2024年2月5日第十二天编程训练&#xff0c;今天主要是进行一些题训练&#xff0c;包括简单题3道、中等题2道和…

21种matlab信号分解方法汇总

21中信号分解方法汇总 CEEMD(互补集合经验模态分解)CEEMDAN(自适应噪声完备集合经验模态分解) EEMD(集合经验模态分解&#xff09;EMD(经验模态分解)ESMD(极点对称模态分解&#xff09;EWT(经验小波变换分解)FEEMD(快速EEMD分解)ICEEMDAN(改进自适应噪声完备集合经验模态分解)L…

微波炉维修笔记

微波主要是靠2.45GHz 左右的微波(12.2cm 波长)加热水分子实现食物加热 所有不要使用金属器皿进行加热&#xff0c;要么因为电磁屏蔽&#xff0c;起不到加热效果&#xff0c;要么火光四射 微波炉基本组成 借鉴姜师傅的视频 碰到不加热其它都正常的问题 1.检查高压电容 使用万…

安装luajit及使用python运行lua脚本

使用Python运行lua脚本前&#xff0c;需要先安装LuaJIT&#xff0c;LuaJIT的官网是下载 (luajit.org) 目前已不再使用.exe文件的下载方式&#xff0c;需要使用Git从公共仓库下载源码&#xff0c;git命令为&#xff1a; $ git clone https://luajit.org/git/luajit.git 下载后…

解决ubuntu登录密码问题

解决ubuntu登录密码问题 不要随便删除密码&#xff0c;不要随便改密码&#xff0c;很容导致密码过期&#xff0c;或者密码无效。参考了很多人的做法&#xff0c;都没有得到解决。下面的过程&#xff0c;够详细了&#xff0c;我就是这么搞好的。 1、重启虚拟机&#xff0c;不停…

数据的力量:构筑现代大型网站之数据库基础与应用

目录 数据库基础知识--前言 大型网站架构特点 DBA数据库管理员 什么是数据? 数据存储 什么是数据库 数据表的概念 为什么需要mysql这样的数据库管理工具&#xff1f;★ DBMS 收费数据库与免费数据库 运维和数据库 开发与运维的不同阶段 数据库类别 数据库具体应用…

数据结构第十六天(二叉树层序遍历/广度优先搜索(BFS)/队列使用)

目录 前言 概述 接口 源码 测试函数 运行结果 往期精彩内容 前言 从前的日色变得慢&#xff0c;车&#xff0c;马&#xff0c;邮件都慢&#xff0c;一生,只够爱一个人。 概述 二叉树的层序遍历可以使用广度优先搜索&#xff08;BFS&#xff09;来实现。具体步骤如下&…

Open CASCADE学习|布尔运算后消除内部拓扑

在CAD建模中&#xff0c;布尔运算是一种逻辑运算方法&#xff0c;通过这种方法&#xff0c;可以创建、修改或组合几何对象。布尔运算主要包括并集&#xff08;UNION&#xff09;、交集&#xff08;INTERSECT&#xff09;和差集&#xff08;SUBTRACT&#xff09;三种运算。 并集…

React18原理: React核心对象之ReactElement对象和Fiber对象

React中的核心对象 在React应用中&#xff0c;有很多特定的对象或数据结构.了解这些内部的设计&#xff0c;可以更容易理解react运行原理列举从react启动到渲染过程出现频率较高&#xff0c;影响范围较大的对象&#xff0c;它们贯穿整个react运行时 如 ReactElement 对象如 Fi…

「函数小课堂」~(C语言)

先赞后看&#xff0c;不足指正! 这将对我有很大的帮助&#xff01; 所属专栏&#xff1a;C语言知识 阿哇旭的主页&#xff1a;Awas-Home page 目录 引言 1. 函数的概念 2. 函数的类型 2.1 库函数 2.2 自定义函数 2.2.1 语法 2.2.2 举例 3. 函数的参数 3.1 实参 3.2 形参…

International Marine Purchasing Association 7(IMPA7)

​IMPA7物料 物料手持下载链接&#xff1a;https://pan.baidu.com/s/13m0EELpEZWlL55UhsRvI5g?pwd8888 提取码&#xff1a;8888 国际船舶物料准则International Marine Purchasing Association 7(IMPA7)。 IMPA是英文“International Marine Purchasing Association”缩写…

vue-vuex(七)

阅读文章你可以收获&#xff1a; 1 了解什么是vuex 2 知道如何在vue项目中集成vuex 3 知道vuex由什么组成&#xff0c;并如何使用 vuex概述 1. 是什么&#xff1a;vuex 是一个 vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;vuex 是一个插件&#xff0…

Swing程序设计(10)列表框,文本框,文本域,密码框

文章目录 前言一、列表框二、文本框&#xff08;域&#xff09; 1.文本框2.文本域三、密码框总结 前言 该篇文章简单介绍了Java中Swing组件里的列表框、文本框、密码框。 一、列表框 列表框&#xff08;JList&#xff09;相比下拉框&#xff0c;自身只是在窗体上占据固定的大小…