美团:Java线程池实现原理 阅读笔记

Java线程池实现原理及其在美团业务中的实践
多核CPU
多线程并行计算,提升服务器性能
J.U.C提供的线程池ThreadPoolExecutor类,帮助开发人员管理线程并方便地执行并行任务

一、写在前面

线程池是什么

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。(管理线程而获取并发性)

池化思想:为了最大化收益并最小化风险,而将资源统一在一起管理的一种思想。

Pooling is the grouping together of resources (assets, equipment, personnel, effort, etc.) for the purposes of maximizing advantage or minimizing risk to the users. The term is used in finance, computing and equipment management.——wikipedia
“池化”思想不仅仅能应用在计算机领域,在金融、设备、人员管理、工作管理等领域也有相关的应用。

线程池解决的问题是什么

线程池解决的核心问题就是资源管理问题
在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:

  1. 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
  2. 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
  3. 系统无法合理管理内部的资源分布,会降低系统的稳定性。(隔离线程环境,如交易服务和搜索服务,分别开启两个线程池)

在计算机领域中的表现为:统一管理IT资源,包括服务器、存储、和网络资源等等。通过共享资源,使用户在低投入中获益。除去线程池,还有其他比较典型的几种使用策略包括:

  1. 内存池(Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎片。
  2. 连接池(Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。
  3. 实例池(Object Pooling):循环使用对象,减少资源在初始化和释放时的昂贵损耗。

二、线程池核心设计与实现

基于JDK 1.8的源码来分析Java线程池的核心设计与实现
美团:Java线程池实现原理 阅读笔记_第1张图片
ThreadPoolExecutor实现的顶层接口是Executor
Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。
ExecutorService接口增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。

AbstractExecutorService则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。最下层的实现类ThreadPoolExecutor实现最复杂的运行部分,ThreadPoolExecutor将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。
美团:Java线程池实现原理 阅读笔记_第2张图片
内部 生产者/消费者模型,将线程和任务两者解耦,从而良好的缓冲任务,复用线程
线程池的运行主要分成两部分:任务管理、线程管理
任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:
(1)直接申请线程执行该任务;
(2)缓冲到队列中等待线程执行;
(3)拒绝该任务。
线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

按三个部分去详细讲解线程池运行机制:

  1. 线程池如何维护自身状态。
  2. 线程池如何管理任务。
  3. 线程池如何管理线程。

生命周期管理

线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount)。在具体实现中,线程池将运行状态(runState)、线程数量 (workerCount)两个关键参数的维护放在了一起
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl这个AtomicInteger类型,是对线程池的运行状态和线程池中有效线程的数量进行控制的一个字段, 它同时包含两部分的信息:线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount),高3位保存runState,低29位保存workerCount,两个变量之间互不干扰。
用一个变量去存储两个值,可避免在做相关决策时,出现不一致的情况,不必为了维护两者的一致,而占用锁资源。

通过阅读线程池源代码也可以发现,经常出现要同时判断线程池运行状态和线程数量的情况。线程池也提供了若干方法去供用户获得线程池当前的运行状态、线程个数。这里都使用的是位运算的方式,相比于基本运算,速度也会快很多。

//获取生命周期状态、获取线程池线程数量
private static int runStateOf(int c)     { return c & ~CAPACITY; } //计算当前运行状态
private static int workerCountOf(int c)  { return c & CAPACITY; }  //计算当前线程数量
private static int ctlOf(int rs, int wc) { return rs | wc; }   //通过状态和线程数生成ctl

ThreadPoolExecutor的运行状态有5种

  • RUNNING
  • SHUTDOWN
  • STOP:
  • tidying:
  • terminated

任务执行机制

任务调度:execute()执行情况

美团:Java线程池实现原理 阅读笔记_第3张图片
首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
1)如果当前运行的线程workerCount 全局锁)。
2)如果workerCount >=corePoolSize,则将任务加入BlockingQueue。
3)如果workerCount >= corePoolSize && workerCount < maximumPoolSize无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(需要获取全局锁)。
4)如果创建新线程将使workerCount >maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁 (严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。

jdk版本不同会有区别
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
         //只有当前线程池中线程数poolSize 
        //当poolSize >=corePoolSize 或线程创建失败,则将当前任务放到工作队列中。  
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {   
           // 校验线程池是否是Running状态且任务是否成功放入workQueue(阻塞队列)
            if (runState == RUNNING && workQueue.offer(command)) {
                //double-check RUNNING状态 
                //如果线程池不处于运行中或任务无法放入队列,并且当前线程数量小于最大允许的线程数量,则创建一个线程执行任务。
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }   
            else if (!addIfUnderMaximumPoolSize(command))
           		 //抛出RejectedExecutionException异常
                reject(command); // is shutdown or saturated
        }
    }

工作线程: 线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行(不会被GC)。Worker类的run()方法:

 public void run() {
	    try {
	    	Runnable task = firstTask;
	    	firstTask = null;
	    	while (task != null || (task = getTask()) != null) {
		    	runTask(task);
		    	task = null;
	    	}
	    } finally {
	    	workerDone(this);
    	}
    }

任务缓冲

美团:Java线程池实现原理 阅读笔记_第4张图片
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列:

  • 在队列为空时,获取元素的线程会等待队列变为非空。
  • 当队列满时,存储元素的线程会等待队列可用。
    生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程

阻塞队列成员:
美团:Java线程池实现原理 阅读笔记_第5张图片

任务申请

任务的执行有两种可能:

  1. 任务直接由新创建的线程执行。
  2. 线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。
    第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。

线程需要从任务缓存模块中不断地取任务执行,帮助线程从阻塞队列中获取任务,实现线程管理模块和任务管理模块之间的通信。这部分策略由getTask方法实现
美团:Java线程池实现原理 阅读笔记_第6张图片
getTask这部分进行了多次判断,为的是控制线程的数量,使其符合线程池的状态

任务拒绝

RejectedExecutionHandler(拒绝策略):当队列和线程池都满了(达到maximumPoolSize),说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
名称 描述 使用场景
AbortPolicy(默认) 丢弃任务,抛出RejectedExecutionException异常。在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。 关键业务使用,在系统不能承载更大的并发量的时候,能够及时的通过异常发现
CallerRunsPolicy 使用 【调用者 dubbo生产者主线程】所在线程(execute 方法的调用线程) 来运行任务(可能会影响主线程)。并非是由线程池的线程处理 需要让所有任务都执行完毕,适合大量计算的任务类型。
DiscardOldestPolicy A handler for rejected tasks that discards the oldest unhandled request and then retries丢弃队列里最前面(最早)的任务,然后重新提交被拒绝的任务 衡量实际业务是否允许丢弃老任务
DiscardPolicy A handler for rejected tasks that silently discards the rejected task.不处理(不抛出异常),丢弃任务。 一些无关紧要的业务

也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化存储不能处理的任务。

补充:生命周期流程源码解读

深入Java线程池:从设计思想到源码解读
向线程池提交任务是用ThreadPoolExecutor的execute()方法,但在其内部,线程任务的处理,涉及到ThreadPoolExecutor、Worker、Thread三个类的6个方法:
美团:Java线程池实现原理 阅读笔记_第7张图片

execute()

在ThreadPoolExecutor类中,,任务提交方法的入口是execute(Runnable command)方法(submit()方法也是调用了execute()),该方法其实只在尝试做一件事:经过各种校验之后,调用 addWorker(Runnable command待执行的任务,boolean core)方法为线程池创建一个线程并执行任务,与之相对应,execute() 的结果有两个:

//JDK1.8版本
/**
 * 在将来的某个时候执行给定的任务。任务可以在新线程中执行,也可以在现有的池线程中执行。
 * 如果由于此执行器已关闭或已达到其容量而无法提交任务以供执行,则由当前的{@code RejectedExecutionHandler}处理该任务。
 * @param command the task to execute  待执行的任务命令
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
	/*
  	 *  Proceed in 3 steps:
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     * 如果运行线程workerCount
    int c = ctl.get(); //得到线程池的当前线程数
    // 步骤1:判断线程池当前线程数是workerCount否小于corePoolSize
    if (workerCountOf(c) < corePoolSize) {
        // 增加一个工作线程并添加任务,成功则返回,否则进行步骤2
        // true代表使用coreSize作为边界约束,否则使用maximumPoolSize
        if (addWorker(command, true))//创建新的线程执行任务
            return;
        c = ctl.get();
    }
    // 步骤1不满足workerCount < corePoolSize,说明已经无法再创建核心线程,那么考虑将任务放入阻塞队列,等待执行完任务的线程来处理
    //步骤2:
    //校验线程池是否是Running状态(只有Running状态的线程池可以接受新任务)
    //任务是否成功放入workQueue【如果任务添加到任务队列成功则进入步骤3,失败则进入步骤4;】
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //步骤3
        //1.再次校验,如果线程池非Running且从任务队列中移除任务成功,则拒绝该任务
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //2. 线程池Running状态;如果线程池工作线程workerCount=0
        else if (workerCountOf(recheck) == 0)
           //添加一个没有初始任务的普通线程(maximumPool),(这个线程将去获取已经加入任务队列的本次任务并执行)
            addWorker(null, false);//线程池不是Running状态,是加入不进去的
    }
    // 步骤4:尝试扩容maximumPoolSize后再次addWorker,失败则拒绝任务
    else if (!addWorker(command, false))
        reject(command);
}

美团:Java线程池实现原理 阅读笔记_第8张图片

addWorker

美团:Java线程池实现原理 阅读笔记_第9张图片

 /**  向线程池添加一个带有任务的工作线程。
     * Checks if a new worker can be added with respect to current
     * pool state and the given bound (either core or maximum). If so,
     * the worker count is adjusted accordingly, and, if possible, a
     * new worker is created and started, running firstTask as its
     * first task. This method returns false if the pool is stopped or
     * eligible to shut down. It also returns false if the thread
     * factory fails to create a thread when asked.  If the thread
     * creation fails, either due to the thread factory returning
     * null, or due to an exception (typically OutOfMemoryError in
     * Thread.start()), we roll back cleanly.
     *
     * @param firstTask the task the new thread should run first (or
     * null if none). 新创建的线程应该首先运行的任务(如果没有,则为空)
     * Workers are created with an initial first task
     * (in method execute()) to bypass queuing when there are fewer
     * than corePoolSize threads (in which case we always start one),
     * or when the queue is full (in which case we must bypass queue).
     * Initially idle threads are usually created via
     * prestartCoreThread or to replace other dying workers.
     *
     * @param core if true use corePoolSize as bound, else
     * maximumPoolSize. (A boolean indicator is used here rather than a
     * value to ensure reads of fresh values after checking other pool
     * state).决定了线程池容量的约束条件
     * true使用corePoolSize 
     * false使用maximumPoolSize
     * @return true if successful
     */
private boolean addWorker(Runnable firstTask, boolean core) {
    // 外层循环:判断线程池状态
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
         //rs >= SHUTDOWN(SHUTDOWN=0)线程池为非Running=-1状态(Running状态则既可以新增核心线程也可以接受任务)
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()))
            return false;

		// 内层循环:向线程池添加线程并返回是否添加成功的结果。
        for (;;) {
            int wc = workerCountOf(c);
            // 校验线程池已有线程数量是否超限:最大上限CAPACITY 
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) 
                return false;
            // 通过CAS操作使工作线程数+1,跳出外层循环
            if (compareAndIncrementWorkerCount(c)) 
                break retry;
            // 线程+1失败,重读ctl
            c = ctl.get();   // Re-read ctl
            // 如果此时线程池状态不再是running,则重新进行外层循环
            if (runStateOf(c) != rs)
                continue retry;
            // 其他 CAS 失败是因为工作线程数量改变了,继续内层循环尝试CAS对线程数+1
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
	//线程数量+1成功的后续操作:添加到工作线程集合,并启动工作线程 
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        final ReentrantLock mainLock = this.mainLock;
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 下面代码需要加锁:线程池主锁
            mainLock.lock(); 
            try {
                // 持锁期间重新检查,线程工厂创建线程失败或获取锁之前关闭的情况发生时,退出
                int c = ctl.get();
                int rs = runStateOf(c);

				// 再次检验线程池是否是running状态或线程池shutdown但线程任务为空
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    // 线程已经启动,则抛出非法线程状态异常
                    // 为什么会存在这种状态呢?未解决
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w); //加入线程池
                    int s = workers.size();
                    // 如果当前工作线程数超过线程池曾经出现过的最大线程数,刷新后者值
                    if (s > largestPoolSize)
                        largestPoolSize = s; 
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();  // 释放锁
            }
            if (workerAdded) { // 工作线程添加成功,启动该线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        //线程启动失败,则进入addWorkerFailed
        if (! workerStarted) 
            addWorkerFailed(w);
    }
    return workerStarted;
}

Worker类

private final class Worker extends AbstractQueuedSynchronizer
    implements Runnable{
    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread; 
     
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
     
    /** Per-thread task counter */
    volatile long completedTasks;
 
    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    // 通过构造函数初始化,
    Worker(Runnable firstTask) {
        //设置AQS的同步状态
        // state:锁状态,-1为初始值,0为unlock状态,1为lock状态
        setState(-1); // inhibit interrupts until runWorker  在调用runWorker前,禁止中断
        this.firstTask = firstTask;
        // 线程工厂创建一个线程
        this.thread = getThreadFactory().newThread(this); 
    }
 
    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this); //runWorker()是ThreadPoolExecutor的方法
    }
 
    // Lock methods
    // The value 0 represents the unlocked state. 0代表“没被锁定”状态
    // The value 1 represents the locked state. 1代表“锁定”状态
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }
 
    /**
     * 尝试获取锁的方法
     * 重写AQS的tryAcquire(),AQS本来就是让子类来实现的
     */
    protected boolean tryAcquire(int unused) {
        // 判断原值为0,且重置为1,所以state为-1时,锁无法获取。
        // 每次都是0->1,保证了锁的不可重入性
        if (compareAndSetState(0, 1)) {
            // 设置exclusiveOwnerThread=当前线程
            setExclusiveOwnerThread(Thread.currentThread()); 
            return true;
        }
        return false;
    }
 
    /**
     * 尝试释放锁
     * 不是state-1,而是置为0
     */
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null); 
        setState(0);
        return true;
    }
 
    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
 
    /**
     * 中断(如果运行)
     * shutdownNow时会循环对worker线程执行
     * 且不需要获取worker锁,即使在worker运行时也可以中断
     */
    void interruptIfStarted() {
        Thread t;
        //如果state>=0、t!=null、且t没有被中断
        //new Worker()时state==-1,说明不能中断
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

内部类。Worker工作线程,实现了Runnable接口,并持有一个线程thread,一个初始化的任务firstTask。

thread是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务;firstTask用它来保存传入的第一个任务,这个任务可以有也可以为null。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。
美团:Java线程池实现原理 阅读笔记_第10张图片
线程池需要管理线程的生命周期,需要在线程长时间不运行的时候进行回收。线程池使用一张Hash表去持有线程的引用,这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。这个时候重要的就是如何判断线程是否在运行。

Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。

  1. lock方法一旦获取了独占锁,表示当前线程正在执行任务中。
  2. 如果正在执行任务,则不应该中断线程。
  3. 如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。
  4. 线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。
    美团:Java线程池实现原理 阅读笔记_第11张图片

runWorker

在Worker类中的run方法调用了runWorker方法来执行任务:

  1. while循环不断地通过getTask()方法获取任务。
  2. getTask()方法从阻塞队列中取任务。
  3. 如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态。
  4. 执行任务。
  5. 如果getTask结果为null则跳出循环,执行processWorkerExit()方法,销毁线程。

美团:Java线程池实现原理 阅读笔记_第12张图片

/**
     * Main worker run loop.  Repeatedly gets tasks from queue and
     * executes them, while coping with a number of issues:
     *
     * 1. We may start out with an initial task, in which case we
     * don't need to get the first one. Otherwise, as long as pool is
     * running, we get tasks from getTask. If it returns null then the
     * worker exits due to changed pool state or configuration
     * parameters.  Other exits result from exception throws in
     * external code, in which case completedAbruptly holds, which
     * usually leads processWorkerExit to replace this thread.
     *
     * 2. Before running any task, the lock is acquired to prevent
     * other pool interrupts while the task is executing, and then we
     * ensure that unless pool is stopping, this thread does not have
     * its interrupt set.
     *
     * 3. Each task run is preceded by a call to beforeExecute, which
     * might throw an exception, in which case we cause thread to die
     * (breaking loop with completedAbruptly true) without processing
     * the task.
     *
     * 4. Assuming beforeExecute completes normally, we run the task,
     * gathering any of its thrown exceptions to send to afterExecute.
     * We separately handle RuntimeException, Error (both of which the
     * specs guarantee that we trap) and arbitrary Throwables.
     * Because we cannot rethrow Throwables within Runnable.run, we
     * wrap them within Errors on the way out (to the thread's
     * UncaughtExceptionHandler).  Any thrown exception also
     * conservatively causes thread to die.
     *
     * 5. After task.run completes, we call afterExecute, which may
     * also throw an exception, which will also cause thread to
     * die. According to JLS Sec 14.20, this exception is the one that
     * will be in effect even if task.run throws.
     *
     * The net effect of the exception mechanics is that afterExecute
     * and the thread's UncaughtExceptionHandler have as accurate
     * information as we can provide about any problems encountered by
     * user code.
     *
     * @param w the worker
     */
   // 是线程池中真正处理任务的方法
   //封装的Worker,携带了工作线程的诸多要素,包括Runnable(待处理任务)、lock(锁)、completedTasks(记录线程池已完成任务数)
   final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // allow interrupts
    // new Worker()是state==-1,此处是调用Worker类的tryRelease()方法,将state置为0,而interruptIfStarted()中只有state>=0才允许调用中断
    w.unlock(); 
            
    // 线程退出的原因,true是任务导致,false是线程正常退出
    boolean completedAbruptly = true; 
    try {
        // 当前任务和从任务队列中获取的任务都为空,方停止循环
        while (task != null || (task = getTask()) != null) {
            //上锁可以防止在shutdown()时终止正在运行的worker,而不是应对并发
            w.lock(); 
             
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This  requires a recheck in second case to deal with  shutdownNow race while clearing interrupt
            /**
             * 判断是否为线程添加中断标识(确保只有在线程处于stop状态且wt未中断时,wt才会被设置中断标识)
             * 条件1:线程池状态>=STOP,即STOP或TERMINATED
             * 条件2:一开始判断线程池状态=STOP(以消除该瞬间shutdown方法生效,使线程池处于STOP或TERMINATED)
             */
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt(); //当前线程调用interrupt()中断
            try {
                //执行前置方法(该方法为空方法,由子类实现)
                beforeExecute(wt, task);
                 
                Throwable thrown = null;
                try {
                    task.run();
                } 
                catch (RuntimeException x) {
                    thrown = x; 
                    throw x;
                } 
                catch (Error x) {
                    thrown = x; throw x;
                } 
                catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } 
                finally {
                    //执行后置方法 (该方法为空方法,由子类实现)
                    afterExecute(task, thrown); 
                }
            } 
            finally {
                task = null; 
                //将线程池已完成的任务数+1
                w.completedTasks++; 
                w.unlock(); //释放锁
            }
        }
        // false:即线程不是突然终止
        completedAbruptly = false;
    } 
    finally {
        //进入线程退出程序,处理worker的退出
        processWorkerExit(w, completedAbruptly);
    }
}

getTask

从任务队列(workQueue)中获取 task(Runnable)


/**
     * Performs blocking or timed wait for a task, depending on
     * current configuration settings, or returns null if this worker
     * must exit because of any of:
     * 1. There are more than maximumPoolSize workers (due to
     *    a call to setMaximumPoolSize).
     * 2. The pool is stopped.
     * 3. The pool is shutdown and the queue is empty.
     * 4. This worker timed out waiting for a task, and timed-out
     *    workers are subject to termination (that is,
     *    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
     *    both before and after the timed wait, and if the queue is
     *    non-empty, this worker is not the last thread in the pool.
     *
     * @return task, or null if the worker must exit, in which case
     *         workerCount is decremented
     */
    private Runnable getTask() {
        boolean timedOut = false; //获取最新一次poll是否超时
        // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //SHUTDOWN(可以继续处理任务队列中的任务,不再接受新任务)
            //STOP或TERMINATED状态,则意味着线程池不必再获取任务了
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();//当前线程数量-1
                return null;
            }
            int wc = workerCountOf(c);
            // Are workers subject to culling?
            //allowCoreThreadTimeOut:当前线程是否以keepAliveTime为超时时限等待任务
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;//给当前正在尝试获取任务的工作线程设置阻塞时间限制
			//线程池数量超限制或者时间超限且(任务队列为空或当前线程数>1)
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))//移除工作线程
                    return null;
                continue;
            }
		//获取任务
            try {	
            	// 如果工作线程阻塞时间受限,则使用poll()
            	// poll()设定阻塞时间,而take()无时间限制,直到拿到结果为止
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;//任务为null时
            } catch (InterruptedException retry) {
               // 响应中断,进入下一次循环前将最近获取任务超时状态置为false
                timedOut = false;
            }
        }
    }

美团:Java线程池实现原理 阅读笔记_第13张图片

processWorkerExit

执行线程退出的方法

/**
 * Performs cleanup and bookkeeping for a dying worker. Called
 * only from worker threads. Unless completedAbruptly is set,
 * assumes that workerCount has already been adjusted to account
 * for exit.  This method removes thread from worker set, and
 * possibly terminates the pool or replaces the worker if either
 * it exited due to user task exception or if fewer than
 * corePoolSize workers are running or queue is non-empty but
 * there are no workers.
 *
 * @param w the worker 要结束的工作线程。
 * @param completedAbruptly if the worker died due to user exception
 */
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    /**
     * 1.工作线程-1操作
     * 1)如果completedAbruptly 为true,说明工作线程发生异常,那么将正在工作的线程数量-1
     * 2)如果completedAbruptly 为false,说明工作线程无任务可以执行,由getTask()执行worker-1操作
     */
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    // 2.从线程set集合中移除工作线程,该过程需要加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 将该worker已完成的任务数追加到线程池已完成的任务数
        completedTaskCount += w.completedTasks;
        // HashSet中移除该worker
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    
	// 3.根据线程池状态进行判断是否结束线程池
    tryTerminate();
	
	/**
     * 4.是否需要增加工作线程
     * 线程池状态是running 或 shutdown
     * 如果当前线程是突然终止的,addWorker()
     * 如果当前线程不是突然终止的,但当前线程数量 < 要维护的线程数量,addWorker()
     * 故如果调用线程池shutdown(),直到workQueue为空前,线程池都会维持corePoolSize个线程,然后再逐渐销毁这corePoolSize个线程
     */
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
       if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

线程池中线程的销毁依赖JVM自动的回收,线程池做的工作是根据当前线程池的状态维护一定数量的线程引用,防止这部分线程被JVM回收,当线程池决定哪些线程需要回收时,只需要将其引用消除即可。
Worker被创建出来后,就会不断地进行轮询,然后获取任务去执行,核心线程可以无限等待获取任务,非核心线程要限时获取任务。
当Worker无法获取到任务,也就是获取的任务为空时,循环会结束,Worker会主动消除自身在线程池内的引用。

线程回收的工作是在processWorkerExit方法完成的。
线程回收
事实上,在这个方法中,将线程引用移出线程池就已经结束了线程销毁的部分。但由于引起线程销毁的可能性有很多,线程池还要判断是什么引发了这次销毁,是否要改变线程池的现阶段状态,是否要根据新状态,重新分配线程。

你可能感兴趣的:(并发编程与高并发)