文章目录
- 一、线程池总体设计
- 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));
- mainLock,重量级锁。访问workers Set集合、shutdown前都要先拿到悲观锁,后面再详细说
private final ReentrantLock mainLock = new ReentrantLock();
private final HashSet<Worker> workers = new HashSet<>();
三、任务execute过程
execute是线程池执行任务的方法。
方法内部会维护工作线程的数量
execute 方法处理逻辑:
- 池线程数<corePoolSize,新增woker线程执行当前command
- 如果池线程数>= corePoolSize,先放入阻塞队列,直到队列满
- 阻塞队列满了,才能继续新增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,新增工作线程
- ctl的低28位表示工作线程数量,workerCountOf是获取低28位表示数量
- 如果pool线程数<corePoolSize,则允许新增addWorker(command, true),command是第一个task,true表示是在新增corePoolSize内的线程
- 由于是并发的情况,可能其他线程也在addWorker,调用addWorker可能有两种结果:
- 当前线程的状态和线程数量没有超过,允许新增,新增worker成功,返回true
- addWorker时pool线程数已经超过核心线程数,则不满足创建条件返回fals
- 新增成功,直接return即可
- 如果新增失败了,说明线程状态ctl在addWorker时,pool线程数已经超过了corePoolSize,不允许新增core线程了。重新获取ctl状态
- 执行后续逻辑
(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)。
- 检查保证线程池状态位为RUNNING,尝试放阻塞队列,如果队列不满,则放入成功。如果队列满了,执行第三部分。
- 放入成功后,再次检查状态位,分为两种情况。如果线程不是RUNNING了,则表示线程正在关闭/已经关闭,这时调用remove,从阻塞队列移除任务command。
- 移除成功,调用拒绝策略
- 也可能remove失败。比如可能是被取出执行了,也可能是其他线程调用了shutdownNow,排空了阻塞队列。
- 如果状态位还是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
伪代码如下:
主要分为两步:
- 轻量级自旋锁
- 真正新增工作线程
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自增占坑位
- 外层循环打了retry标签,在里层循环可以直接break或continue外层循环
- 外层循环判断状态位是否允许创建,内层循环主要判断当前数量是否允许创建。不能创建return false。可以创建ctl先+1,先把坑位占住。后续逻辑真正创建线程
- 外层状态位判断,以下三种状态,不能创建线程池:
- STOP状态: 即调用了shutdownNow
- shudown中的状态下,firstTask != null不能创建线程,因为shudown之后不能再加入新的Command
- shudown中的状态下,workQueue.isEmpty(),不需要加入新的线程处理了
- 内层进行workerCount自旋判断:
- 判断woker数是否超过要求。如果线程数达到要求的数量,不能创建,返回false
- compareAndIncrementWorkerCount© 调用CAS对ctl线程数量做+1,如果成功,表明已经把坑占住了,那么break retry终止外层循环,使用后面的逻辑新增线程
- 如果compareAndIncrementWorkerCount不成功,说明ctl被其他线程改过,这里重新读ctl:c = ctl.get()
- 如果ctl状态位是>=SHUDOWN,说明状态变了,需要continue外层循环,通过外层循环校验状态位
- 否则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(),调用后则线程池终止状态 |
firstTask==null
,这种特殊情况下为保证阻塞队列有工作线程消费,所以状态位==shutdown && firstTask==null
的情况需要创建worker线程。调用位置为下:- c < STOP,由状态表可知,即
状态位==shutdown && firstTask==null
。主要时线程池shutdown,阻塞队列还有任务没执行完,这时候时允许增加线程的
// 如果corePoolSize==0,加入第一个任务,如果当前线程为0,则创建一个线程。避免任务队列没有线程去执行。
else if (workerCountOf(recheck) == 0)
// 2.addWorker会判断状态,决定是否创建worker
addWorker(null, false);
真正添加工作线程代码:
- 拿到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)
- worker是一个内部类,实现了Runnable接口,run方法调用外部的runWoker。
- Worker继承了AQS(AbstractQueuedSynchronizer),Worker本身就是一个排他锁。执行任务(Conmand)前 / 中断线程前都要获得Worker的排他锁。
- 构造函数传入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关键代码如下:
- 调用w.unlock(); 调用后status==0,现在允许中断当前线程了
- 如果firstTask不为空,则task就是firstTask。否则调用getTask获取下一个Task,getTask方法会一直阻塞,直到返回任务。getTask返回null,worker线程需要退出。
- woker线程退出前,会不断getTask—》执行–》getTask–》。。,循环下去
- getTask返回null,线程跳出wile循环,工作线程(worker)运行结束
- beforeExecute、afterExecute是执行任务前后的处理,目前是空实现,子类可以拓展
- 最终调用processWorkerExit做一些清理工作
- completedAbruptly表示是否突然终止。如果task抛出异常了,completedAbruptly就是true,异常继续往上层抛,worker线程异常终止
- 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方法是重点
- getTask从队列中拉取下一个需要执行的任务,一旦返回null,woker线程会退出
- 获取任务是一个自旋过程,每次自旋都会检查线程池状态是否达到线程退出状态
- 线程退出状态包括:线程数量超过maximumPoolSize、线程超时keepAliveTime等
- allowCoreThreadTimeOut参数表示允许核心线程idle超时关闭
- 调用阻塞队列的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
- 该方法用于检查线程是否可达到TERMINATED状态,1.
(SHUTDOWN and pool and queue empty)
和2.(STOP and pool empty)
时满足条件,其他情况不做任何处理。 - 拿第一条件来说,shutdown方法被调用,线程池状态位SHUTDOWN,queue中的任务被执行完,pool中线程也全都关闭了。
- 满足条件,状态先转换位TIDYING,去执行hook方法terminated(),执行后正式TERMINATED
- termination.signalAll() 唤醒在awaitTermination的所有线程。这两个方法都会拿到mainLock,没有并发安全问题。
- 这个方法在所有可能会达到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
}
}