说明
线程池如何使用?它是如何回收空闲线程的?这类问题可能在面试中经常遇到。本篇博文我将通过源码来对线程池提交任务及回收空闲线程部分的内容进行学习总结。
我们都知道在向线程池提交任务时,会有以下三种情况:
- 如果线程池内运行的线程数小于设置的 corePoolSize 值,不论是否有空闲线程都会新创建一个线程执行该任务
- 如果线程数已到达 corePoolSize 值,则会将任务放入任务队列
- 当任务队列已满,并且线程数小于设置的 maximumPoolSize 值,则会新创建线程执行该任务,否则将执行设置的拒绝策略
在线程池处理完任务后,会根据设置的 keepAliveTime 来回收核心线程数 corePoolSize 之外的线程,同时若设置了 allowCoreThreadTimeOut 值为 true,也会对核心线程进行回收。
正文
提交任务
这部分代码就是根据以上说明的步骤进行操作的。但需要注意的是,在任务进入任务队列时进行了两次状态检查,这样做的原因是在入队后线程池状态可能发生变化,如 线程池主动停止,这时就需要将任务从队列中删除;线程池中唯一的线程停止,就需要重新添加 woker 来处理任务。
public void execute(Runnable command) {
if (command == null)
int c = ctl.get();
// 线程数量小于核心线程数 直接新添加 worker
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 第一次检查运行状态
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
else if (!addWorker(command, false))
reject(command);
}
添加新 worker
通过调用 addWorker(Runnable task, boolean core) 方法来添加新的工作线程。该方法可以分成两部分,在第一部分通过循环判断线程池的运行状态及完成线程数量的修改。
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 当线程池已关闭或正处于关闭过程,没有新任务且任务队列已空时,不需要再添加新 worker
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&