文章目录

  • 一、线程池总体设计
  • 1.1 为什么需要池化
  • 1.2 用一句话简述Java线程池的设计
  • 二、创建线程池
  • 2.1 线程池构造参数
  • 2.2 其他核心成员变量
  • 三、任务execute过程
  • execute 方法处理逻辑:
  • 3.1 第一部分:
  • 3.2 第二部分:
  • 3.3 第三部分:
  • 四. addWorker方法详解
  • 4.1 线程池的状态位
  • 4.2 addWoker详解
  • 五、Worker的实现
  • 5.1 runWorker方法
  • 5.2 getTask方法
  • 六、 线程池关闭
  • 6.1 shutdown和shutdownNow
  • 6.2 tryTerminate


一、线程池总体设计

1.1 为什么需要池化

池化技术:一些资源预先分配好,组织在池子中,需要使用到资源时从池中获取

池化技术一般用于创建和销毁开销巨大的资源,常用池化技术进行优化,如:连接池、线程池、字符串常量池等,用到再从池中获取,以减小开销

1.2 用一句话简述Java线程池的设计

任务放到阻塞队列中,worker线程从队列中获取任务执行。并且worker线程的数量可以根据一定的策略加大缩小。


二、创建线程池

2.1 线程池构造参数

在这里回顾一下线程池的构造参数

参数

说明

corePoolSize

核心线程数,线程池保留corePoolSize个线程,即使空闲也不会回收。

maximumPoolSize

增加工作线程不能超过maximumPoolSize个

keepAliveTime

工作线程空闲多久可回收。默认会保留核心线程

TimeUnit

keepAliveTime的时间单位

BlockingQueue workQueue

阻塞队列,存储Runnable任务

threadFactory

线程工厂,创建并定制化工作线程的参数。诸如线程名字、线程优先级等

RejectedExecutionHandler

拒绝策略,阻塞队列满的处理策略。

包括:

AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

2.2 其他核心成员变量

1.成员变量ctl:原子Integer作为轻量级锁,这个整数的高3位表示线程池状态,低28位表示工作线程数量workerCount,ctl应该是control的缩写,意思时控制

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  1. mainLock,重量级锁。访问workers Set集合、shutdown前都要先拿到悲观锁,后面再详细说
private final ReentrantLock mainLock = new ReentrantLock();


    private final HashSet<Worker> workers = new HashSet<>();

三、任务execute过程

execute是线程池执行任务的方法。
方法内部会维护工作线程的数量

execute 方法处理逻辑:

  1. 池线程数<corePoolSize,新增woker线程执行当前command
  2. 如果池线程数>= corePoolSize,先放入阻塞队列,直到队列满
  3. 阻塞队列满了,才能继续新增worker线程。线程数不能超过maximumPoolSize,无法放入队列,也无法新增线程,此时线程池已经饱和。将会调用拒绝策略RejectedExecutionHandler

以上过程不难理解。这个过程使用CAS轻量级锁实现:

总体代码如下:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        // 第一部分:池线程数<corePoolSize,新增工作线程
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 第二部分:如果池线程数>= corePoolSize,尝试将任务放入阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 第三部分:尝试新增worker线程。不能超过maximumPoolSize
        else if (!addWorker(command, false))
            reject(command);
    }    }

3.1 第一部分:

池线程数<corePoolSize,新增工作线程

  1. ctl的低28位表示工作线程数量,workerCountOf是获取低28位表示数量
  2. 如果pool线程数<corePoolSize,则允许新增addWorker(command, true),command是第一个task,true表示是在新增corePoolSize内的线程
  3. 由于是并发的情况,可能其他线程也在addWorker,调用addWorker可能有两种结果:
  4. 当前线程的状态和线程数量没有超过,允许新增,新增worker成功,返回true
  5. addWorker时pool线程数已经超过核心线程数,则不满足创建条件返回fals
  6. 新增成功,直接return即可
  7. 如果新增失败了,说明线程状态ctl在addWorker时,pool线程数已经超过了corePoolSize,不允许新增core线程了。重新获取ctl状态
  8. 执行后续逻辑

(addWorker是核心方法,文章后面有讲解,这里只需要知道它是用轻量级锁CAS实现了线程安全即可)

//获取ctl状态
        int c = ctl.get();
        // 如果当前线程数 < corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            // 尝试addWorker
            if (addWorker(command, true))
                // 新增成功,直接return
                return;
            // ctl线程数超过corePoolSize, 重新获取ctl
            c = ctl.get();
        }
        // 下面尝试加入队列

workerCountOf(int c) 获取ctl的线程数

// 低28位标识数量
private static final int COUNT_BITS = Integer.SIZE - 3;
// 0001111111111111111111111111111   & ctl,&位运算得低28位的值
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 获取低28位的值,表示工作线程数量
private static int workerCountOf(int c)  { return c & COUNT_MASK; }

3.2 第二部分:

如果池线程数>= corePoolSize,尝试将任务放入阻塞队列

这里两次检查了ctl状态,双重检查(double-check)。

  1. 检查保证线程池状态位为RUNNING,尝试放阻塞队列,如果队列不满,则放入成功。如果队列满了,执行第三部分。
  2. 放入成功后,再次检查状态位,分为两种情况。如果线程不是RUNNING了,则表示线程正在关闭/已经关闭,这时调用remove,从阻塞队列移除任务command。
  3. 移除成功,调用拒绝策略
  4. 也可能remove失败。比如可能是被取出执行了,也可能是其他线程调用了shutdownNow,排空了阻塞队列。
  5. 如果状态位还是RUNING状态,如果当前工作线程数为0,添加一个工作线程。我认为作用是,当corePoolSize=0时,避免放入queue中,一直没有worker线程执行。如果这里不创建的话,需要等到第三步,队列满才会创建worker线程。
// 第二部分
        // 如果是RUNNINIG状态,才加入队列
        if (isRunning(c) && workQueue.offer(command)) {
            // 加入成功,双重检查状态
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                // 如果状态不为RUNNING,则尝试移除任务,调用拒绝策略
                reject(command);
            // 如果corePoolSize==0,加入第一个任务,如果当前线程为0,则创建一个线程。避免任务队列没有线程去执行。
            else if (workerCountOf(recheck) == 0)
                // addWorker会判断状态,决定是否创建worker
                addWorker(null, false);
        }
       // 如果加入队列失败,说明队列已满,才会考虑创建更多的线程

3.3 第三部分:

队列满了,尝试新增worker线程

尝试新增worker,在addWorker里面,如果超过了maximumPoolSize就不能创建了。创建失败,就执行拒绝策略

else if (!addWorker(command, false))
            reject(command);

四. addWorker方法详解

addWorker增加工作线程。有两个参数,其中firstTask是新增后的线程执行的首个任务,不需要去queue中拿;core参数表示是否核心线程。返回值表示是否创建成功

private boolean addWorker(Runnable firstTask, boolean core){...}

读者不妨回顾一下上文,addWorker在execute中调用三次,参数时有所差异的,参数是不一样的,后面再详细聊聊

//新增核心线程
addWorker(command, true)
//放入阻塞队列,但线程数为0时创建一个线程
addWorker(null, false);
// 添加非核心线程
addWorker(command, true)

4.1 线程池的状态位

需要讲addWorker方法,需要对线程池的状态有个大致的认识。上文说到,ctl高3位时线程池的状态位,用来表示线程池的状态,addWorker需要判断当前状态是否可以新增线程

// 运行状态 -1,这里用位运算移动到高3位
    private static final int RUNNING    = -1 << COUNT_BITS;
    // 调用了shtudown方法,线程池不再接受新的任务,阻塞队列的任务执行完
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 调用shutdownNow方法时,线程池排空阻塞队列。
    private static final int STOP       =  1 << COUNT_BITS;
    // shutdown和stop之后,线程都关闭完之后达到TIDYING    
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 达到TIDYING状态,调用terminated(),调用后则线程池终止状态
    private static final int TERMINATED =  3 << COUNT_BITS;

状态


说明

RUNNING

-1

运行状态

SHUTDOWN

0

调用了shtudown方法,线程池不再接受新的任务,阻塞队列的任务执行完

STOP

1

调用shutdownNow方法时,线程池排空阻塞队列。

TIDYING

2

shutdown和stop之后,线程都关闭完之后达到TIDYING

TERMINATED

3

达到TIDYING状态,调用terminated(),调用后则线程池终止状态

这里注意一下状态位的设计,从小到大有一定的顺序,方便用>=”xx状态“来编程

4.2 addWoker详解

addWorker:线程池增加工作线程

addWorker用CAS自旋的方式尝试增加worker线程,直到创建成功,或者状态不满足返回false

伪代码如下:

主要分为两步:

  1. 轻量级自旋锁
  2. 真正新增工作线程
private boolean addWorker(Runnable firstTask, boolean core) {
        // 1.轻量级自旋锁:
        retry:
        for (int c = ctl.get();;) {
            if (判断线程状态位不符合要求)
                return false;
            // 状态满足
            for (;;) {
                if (不满足corePoolSize或maximumPoolSize要求) {
                    return false
                }
                if (内层循环尝试ctl workerCount位CAS自增成功) {
                   break retry;
                }
                // 继续自旋
            }
        }

        // 2.真正新增工作线程:
        // 加悲观锁创建线程,并加入workers set。
        // 前面提到,任何时候访问workers set都必须先拿到悲观锁
    }

(1)轻量级锁自旋,先workerCount自增占坑位

  1. 外层循环打了retry标签,在里层循环可以直接break或continue外层循环
  2. 外层循环判断状态位是否允许创建,内层循环主要判断当前数量是否允许创建。不能创建return false。可以创建ctl先+1,先把坑位占住。后续逻辑真正创建线程
  3. 外层状态位判断,以下三种状态,不能创建线程池:
  4. STOP状态: 即调用了shutdownNow
  5. shudown中的状态下,firstTask != null不能创建线程,因为shudown之后不能再加入新的Command
  6. shudown中的状态下,workQueue.isEmpty(),不需要加入新的线程处理了
  7. 内层进行workerCount自旋判断:
  8. 判断woker数是否超过要求。如果线程数达到要求的数量,不能创建,返回false
  9. compareAndIncrementWorkerCount© 调用CAS对ctl线程数量做+1,如果成功,表明已经把坑占住了,那么break retry终止外层循环,使用后面的逻辑新增线程
  10. 如果compareAndIncrementWorkerCount不成功,说明ctl被其他线程改过,这里重新读ctl:c = ctl.get()
  11. 如果ctl状态位是>=SHUDOWN,说明状态变了,需要continue外层循环,通过外层循环校验状态位
  12. 否则CAS失败是由于只有数量发生了改变,继续自旋自增workerCount
// 状态 大于等于
private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
}
//状态小于
private static boolean runStateLessThan(int c, int s) {
        return c < s;
}

状态


说明

RUNNING

-1

运行状态

SHUTDOWN

0

调用了shtudown方法,线程池不再接受新的任务,阻塞队列的任务执行完

STOP

1

调用shutdownNow方法时,线程池排空阻塞队列。

TIDYING

2

shutdown和stop之后,线程都关闭完之后达到TIDYING

TERMINATED

3

达到TIDYING状态,调用terminated(),调用后则线程池终止状态

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (int c = ctl.get();;) {
            // c >= 0 && (c >= 1 || firstTask != null || workQueue.isEmpty()) 
            // 不允许创建worker线程
            // 1.线程在STOP及以上状态,不再创建worker
            // 2.SHUTDOWN状态,firstTask != null说明执行新的任务,SHUTDOWN拒绝新任务
            // 3.SHUTDOWN状态,workQueue已经空了,不能创建worker了
            // 除异常三种情况,如SHTDOWN状态下,workQueue还有任务,新增非核心线程则允许新增
			// 判断是否允许增加woker线程
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;
            // 状态满足
            for (;;) {
                // 内层循环判断线程数量
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                // 线程数量尝试CAS自增
                if (compareAndIncrementWorkerCount(c))
                    // 自增成功,终止外层循环,高高兴兴去后续创建worker线程
                    break retry;
                // 自增失败,因为其他线程改了ctl,这里重新读取ctl
                c = ctl.get();  // Re-read ctl
                // 如果状态是>=shutdown,状态可能位改了
                if (runStateAtLeast(c, SHUTDOWN))
                    //跳到外层校验状态    
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
                // 执行到这里,只有workerCount发生了改变,所以只需要重试内层循环即可
            }
        }
}

(2)占了坑位后,真正新增工作线程

这一部分相对较好理解。拿到悲观锁,创建worker,加入workers set等,最终启动线程。

其中着重讲下,为什么runStateLessThan(c, STOP) && firstTask == null的情况也允许新增线程?

状态


说明

RUNNING

-1

运行状态

SHUTDOWN

0

调用了shtudown方法,线程池不再接受新的任务,阻塞队列的任务执行完

STOP

1

调用shutdownNow方法时,线程池排空阻塞队列。

TIDYING

2

shutdown和stop之后,线程都关闭完之后达到TIDYING

TERMINATED

3

达到TIDYING状态,调用terminated(),调用后则线程池终止状态

  1. firstTask==null,这种特殊情况下为保证阻塞队列有工作线程消费,所以状态位==shutdown && firstTask==null的情况需要创建worker线程。调用位置为下:
  2. c < STOP,由状态表可知,即状态位==shutdown && firstTask==null。主要时线程池shutdown,阻塞队列还有任务没执行完,这时候时允许增加线程的
// 如果corePoolSize==0,加入第一个任务,如果当前线程为0,则创建一个线程。避免任务队列没有线程去执行。
         else if (workerCountOf(recheck) == 0)
             // 2.addWorker会判断状态,决定是否创建worker
             addWorker(null, false);

真正添加工作线程代码:

  1. 拿到mainLock,检查线程状态通过后,随即加入workers集合,并启动线程。这里不再详细说明,读者感兴趣可自行阅读代码
private boolean addWorker(Runnable firstTask, boolean core) {
    	// 省略上面的判断轻量级锁自旋,先workerCount自增占坑位
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();

                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
}

五、Worker的实现

在这里,我先提出一个问题,请读者思考一下,Runable执行过程中抛出异常,woker会异常终止嘛?线程池可以怎么处理这种异常呢?

每个Worker代表了一个工作线程,它不断地从阻塞队列取出任务运行,并维护自身地生命周期(线程池参数:keepAliveTime)

  1. worker是一个内部类,实现了Runnable接口,run方法调用外部的runWoker。
  2. Worker继承了AQS(AbstractQueuedSynchronizer),Worker本身就是一个排他锁。执行任务(Conmand)前 / 中断线程前都要获得Worker的排他锁。
  3. 构造函数传入firstTask。使用工厂new了新线程,传入当前Worker作为线程启动后的第一个任务。在构造方法里setState(-1),这里表示不允许中断,涉及AQS,并且这里也不影响理解Woker,有机会后面再详细说说AQS,这里不做深入讨论。

在addWorker方法中,新增线程后,会启动线程,run方法只是调用了外部的runWoker方法

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        final Thread thread;

        Runnable firstTask;

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        public void run() {
            runWorker(this);
        }

        //省略了AQS加锁方法的实现。status为0表示unlock,1表示lock。涉及AQS,不详细说
    }

5.1 runWorker方法

不断调用getTask从阻塞队列拿任务执行,如getTask返回null,工作线程超时退出

runWorker关键代码如下:

  1. 调用w.unlock(); 调用后status==0,现在允许中断当前线程了
  2. 如果firstTask不为空,则task就是firstTask。否则调用getTask获取下一个Task,getTask方法会一直阻塞,直到返回任务。getTask返回null,worker线程需要退出。
  3. woker线程退出前,会不断getTask—》执行–》getTask–》。。,循环下去
  4. getTask返回null,线程跳出wile循环,工作线程(worker)运行结束
  5. beforeExecute、afterExecute是执行任务前后的处理,目前是空实现,子类可以拓展
  6. 最终调用processWorkerExit做一些清理工作
  7. completedAbruptly表示是否突然终止。如果task抛出异常了,completedAbruptly就是true,异常继续往上层抛,worker线程异常终止
  8. worker运行任务前先拿到当前worker的lock,表示正在执行任务,调用shutdown时不会中断,后面说关闭也会提到。
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        	// getTask不断获取任务。如果返回null,退出循环,工作线程退出
            while (task != null || (task = getTask()) != null) {
                // 所有worker
                w.lock();
                // 省略部分代码,某些情况需要中断线程
                try {
                    beforeExecute(wt, task);
                    try {
                        task.run();
                        afterExecute(task, null);
                    } catch (Throwable ex) {
                        afterExecute(task, ex);
                        throw ex;
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

Runable执行过程中抛出异常,woker会异常终止嘛?线程池可以怎么处理这种异常呢?
看到这里可以,runnable抛异常,会往工作线程抛异常。线程退出

5.2 getTask方法

从阻塞队列去除任务,超过keepAliveTime返回null,表示关闭当前worker

getTask方法是重点

  1. getTask从队列中拉取下一个需要执行的任务,一旦返回null,woker线程会退出
  2. 获取任务是一个自旋过程,每次自旋都会检查线程池状态是否达到线程退出状态
  3. 线程退出状态包括:线程数量超过maximumPoolSize、线程超时keepAliveTime等
  4. allowCoreThreadTimeOut参数表示允许核心线程idle超时关闭
  5. 调用阻塞队列的poll方法,如果队列中无任务,则阻塞一段时间
private Runnable getTask() {
        // 是否poll()超时
        boolean timedOut = false; // Did the last poll() time out?            
        
        for (;;) {
            int c = ctl.get();

            // 每次自旋会检查当前线程是否shutdown workQueue为空,或者进入了STOP状态。
            // 这两种情况都不再需要worker线程了,ctl的线程数位-1,return null表示线程退出
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            // 当前woker数量
            int wc = workerCountOf(c);
            
            // Are workers subject to culling?
            // 是否需要空闲超时检测
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            
            //线程数超过maximumPoolSize 或者超时的情况下,都将关闭当前worker
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 如果需要关闭
                // 执行CAS操作-1
                if (compareAndDecrementWorkerCount(c))
                    // CAS成功,关闭当前worker
                    return null;
                // ctl发生了变化,CAS失败,continue继续自旋
                continue;
            }

            try {
                // 执行阻塞队列的poll方法,如果需要超时检测,poll需要加个timeOut
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                // 这里设置位超时,下一次循环会检测是否要清理
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

六、 线程池关闭

6.1 shutdown和shutdownNow

1.shudown方法: 关闭线程池,不再接收新的任务,已经在workQueue的任务继续执行。只调用空闲的worker线程的中断方法

2.shutdownNow方法:不但不接受新任务,还需要排空workQueue。中断也会改成调用所有worker线程中断方法

关闭示例代码如下:

// shutdown,不再接收新的任务,已经在workQueue的任务继续执行。调用空闲的线程的中断方法
        executorService.shutdown();
        boolean terminated;
        try {
            // 等闲线程池终结,最多等待60S,返回是否线程池是否terminated(终结)
            terminated = executorService.awaitTermination(60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            terminated = false;
        }
        // 如果没有终结,调用shutdownNow
        if (!terminated) {
            // shutdownNow,不再接受新任务,排空workQueue,调用所有workers线程的中断方法
            List<Runnable> commands = executorService.shutdownNow();
        }

shutdown

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        // 加悲观锁
        mainLock.lock();
        try {
            // 检查权限,不重要
            checkShutdownAccess();
            // ctl状态改为SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 只中断空闲线程
            interruptIdleWorkers();
            // 默认为空
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }
    
    // 中断空闲的Workers。中断前需要tryLock,讲worker时也有提到,
    // 执行任务的worker tryLock会失败,不调用中断方法
    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

shotdownNow

public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            // 中断所有worker
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

当线程池关闭时,woker线程会被中断,这要求我们要正确处理中断,才能保证woker线程快速退出

6.2 tryTerminate

尝试线程池变为TERMINATED

  1. 该方法用于检查线程是否可达到TERMINATED状态,1.(SHUTDOWN and pool and queue empty)和2. (STOP and pool empty)时满足条件,其他情况不做任何处理。
  2. 拿第一条件来说,shutdown方法被调用,线程池状态位SHUTDOWN,queue中的任务被执行完,pool中线程也全都关闭了。
  3. 满足条件,状态先转换位TIDYING,去执行hook方法terminated(),执行后正式TERMINATED
  4. termination.signalAll() 唤醒在awaitTermination的所有线程。这两个方法都会拿到mainLock,没有并发安全问题。
  5. 这个方法在所有可能会达到TERMINATED的地方被调用,用CAS机制,保证了线程池的terminated方法只会被执行一次
final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateLessThan(c, STOP) && ! workQueue.isEmpty()))
                return;
            // 如果worker数不为0
            if (workerCountOf(c) != 0) { // Eligible to terminate
                // 调用一个空闲线程的中断方法。那个空闲线程退出后也会调用tryTerminate,
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                 // 状态变为TIDYING,表示整理状态
                 if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        // 执行hook方法terminated,默认实现为空,子类可重写
                        terminated();
                    } finally {
                        // 执行完了,状态就设置为TERMINATED,线程池状态正式终结
                        ctl.set(ctlOf(TERMINATED, 0));
                        // 唤醒在awaitTermination等待的所有线程
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }