Java线程池实现原理详解

Java线程池实现原理详解

  • 一、为什么要使用线程池?
  • 二、线程池的原理?
    • 1、线程池的七大参数
    • 2、线程池的工作原理
      • 2.1 线程池工作原理步骤
      • 2.2 线程池任务提交流程图
    • 3、线程池的底层原理
      • 3.1 ThreadPoolExecutor中的全局常量和方法
      • 3.2 ThreadPoolExecutor类中的相关代码
        • 3.2.1 提交任务相关代码
        • 3.2.2 Worker的结构
        • 3.2.3 添加Callable任务的实现源码
        • 3.2.4 shutdown和shutdownNow方法的实现
  • 三、如何使用线程池?
    • 1. 线程池的分类
      • 1.1 newFixedThreadPool
      • 1.2 newSingleThreadExecutor
      • 1.3 newCachedThreadPool
      • 1.4 newScheduledThreadPool
    • 2. 阻塞队列

一、为什么要使用线程池?

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

二、线程池的原理?

1、线程池的七大参数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  1. corePoolSize: 规定线程池有几个线程(worker)在运行。
  2. maximumPoolSize: 当workQueue满了,不能添加任务的时候,这个参数才会生效。规定线程池最多只能有多少个线程(worker)在执行。
  3. keepAliveTime: 超出corePoolSize大小的那些线程的生存时间,这些线程如果长时间没有执行任务并且超过了keepAliveTime设定的时间,就会消亡。
  4. unit: 生存时间对于的单位
  5. workQueue: 存放任务的队列
  6. threadFactory: 创建线程的工厂
  7. handler: 当workQueue已经满了,并且线程池线程数已经达到maximumPoolSize,将执行拒绝策略。

Java线程池实现原理详解_第1张图片

2、线程池的工作原理

2.1 线程池工作原理步骤

  1. 通过execute方法提交任务时,当线程池中的线程数小于corePoolSize时,新提交的任务将通过创建一个新线程来执行,即使此时线程池中存在空闲线程。
    Java线程池实现原理详解_第2张图片
  2. 通过execute方法提交任务时,当线程池中线程数量达到corePoolSize时,新提交的任务将被放入workQueue中,等待线程池中线程调度执行。
    Java线程池实现原理详解_第3张图片
  3. 通过execute方法提交任务时,当workQueue已存满,且maximumPoolSize大于corePoolSize时,新提交的任务将通过创建新线程执行。
    Java线程池实现原理详解_第4张图片
  4. 当线程池中的线程执行完任务空闲时,会尝试从workQueue中取头结点任务执行。
    Java线程池实现原理详解_第5张图片
  5. 通过execute方法提交任务,当线程池中线程数达到maxmumPoolSize,并且workQueue也存满时,新提交的任务由RejectedExecutionHandler执行拒绝操作。
    Java线程池实现原理详解_第6张图片
  6. 当线程池中线程数超过corePoolSize,并且未配置allowCoreThreadTimeOut=true,空闲时间超过keepAliveTime的线程会被销毁,保持线程池中线程数为corePoolSize。
    Java线程池实现原理详解_第7张图片
  7. 当设置allowCoreThreadTimeOut=true时,任何空闲时间超过keepAliveTime的线程都会被销毁。
    Java线程池实现原理详解_第8张图片

2.2 线程池任务提交流程图

Java线程池实现原理详解_第9张图片
注意:

  1. 当workQueue使用的是无界限队列时,maximumPoolSize参数就变的无意义了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
  2. 使用SynchronousQueue队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。
  3. maximumPoolSize要设置大一点。核心线程和最大线程数量相等时keepAliveTime无作用.

3、线程池的底层原理

3.1 ThreadPoolExecutor中的全局常量和方法

//这个属性是用来存放 当前运行的worker数量以及线程池状态的
//int是32位的,这里把int的高3位拿来充当线程池状态的标志位,后29位拿来充当当前运行worker的数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//存放任务的阻塞队列
private final BlockingQueue<Runnable> workQueue;
//worker的集合,用set来存放
private final HashSet<Worker> workers = new HashSet<Worker>();
//历史达到的worker数最大值
private int largestPoolSize;
//当队列满了并且worker的数量达到maxSize的时候,执行具体的拒绝策略
private volatile RejectedExecutionHandler handler;
//超出coreSize的worker的生存时间
private volatile long keepAliveTime;
//常驻worker的数量
private volatile int corePoolSize;
//最大worker的数量,一般当workQueue满了才会用到这个参数
private volatile int maximumPoolSize;
  1. ctl用于表示线程池的状态线程数,在ThreadPoolExecutor中使用32位二进制数来表示线程池的状态和线程池中线程数量,其中前3位表示线程池状态后29位表示线程池中线程数。private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0))初始化线程池状态为RUNNING、线程池数量为0。
    在这里插入图片描述
  2. COUNT_BITS值等于Integer.SIZE - 3,在源码中Integer.SIZE是32,所以COUNT_BITS=29。CAPACITY表示线程池允许的最大线程数,转算后的结果如下。
    在这里插入图片描述
  3. RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED分别表示线程池的不同状态,转算后的结果如下。
    Java线程池实现原理详解_第10张图片
  4. 线程池处在不同的状态时,它的处理能力是不同的。
    Java线程池实现原理详解_第11张图片
  5. 线程池不同状态之间的转换时机及转换关系如下图。
    Java线程池实现原理详解_第12张图片
  6. runStateOf获取ctl高三位,也就是线程池的状态。workerCountOf获取ctl低29位,也就是线程池中线程数。ctlOf计算ctlOf新值,也就是线程池状态和线程池个数。

3.2 ThreadPoolExecutor类中的相关代码

3.2.1 提交任务相关代码

Java线程池实现原理详解_第13张图片

Java线程池实现原理详解_第14张图片

3.2.2 Worker的结构

Worker是ThreadPoolExecutor内部定义的一个内部类。我们先看一下Worker的继承关系。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable

它实现了Runnable接口,所以可以拿来当线程用。同时它还继承了AbstractQueuedSynchronizer同步器类,主要用来实现一个不可重入的锁。
一些属性还有构造方法:

//运行的线程,前面addWorker方法中就是直接通过启动这个线程来启动这个worker
final Thread thread;
//当一个worker刚创建的时候,就先尝试执行这个任务
Runnable firstTask;
//记录完成任务的数量
volatile long completedTasks;
Worker(Runnable firstTask) {
   
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            //创建一个Thread,将自己设置给他,后面这个thread启动的时候,也就是执行worker的run方法
            this.thread = getThreadFactory().newThread(this);
}

worker的run方法

public void run() {
   
            //这里调用了ThreadPoolExecutor的runWorker方法
            runWorker(this);
}

ThreadPoolExecutor的runWorker方法

final void runWorker(Worker w) {
   
        //获取当前线程
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //执行unlock方法,允许其他线程来中断自己
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
   
            //如果前面的firstTask有值,就直接执行这个任务
            //如果没有具体的任务,就执行getTask()方法从队列中获取任务
            //这里会不断执行循环体,除非线程中断或者getTask()返回null才会跳出这个循环
            while (task != null || (task = getTask()) != null) {
   
                //执行任务前先锁住,这里主要的作用就是给shutdown方法判断worker是否在执行中的
                //shutdown方法里面会尝试给这个线程加锁,如果这个线程在执行,就不会中断它
                w.lock();
               //判断线程池状态,如果线程池被强制关闭了,就马上退出
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (

你可能感兴趣的:(java)