ThreadPoolExecutor.execute() 核心源码流程解析
如果你真的读过ThreadPoolExecutor.execute() 源码,还能应用到面试上,面试官一定对你另眼相看!
1. 源码入口(基于 JDK 8)
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
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);
}
else if (!addWorker(command, false))
reject(command);
}
第一步:参数校验
if (command == null)
throw new NullPointerException();
- 如果提交的任务是
null,直接抛出空指针异常。 - 目的:保证线程池处理的是有效任务。
第二步:获取当前线程池状态
int c = ctl.get();
ctl是一个高位存线程池运行状态,低位存线程数量的复合变量(int 型,位操作编码)。workerCountOf(c)获取当前有效线程数量。
第三步:核心线程是否能处理?
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get(); // 失败后重新获取状态
}
- 当前运行线程数小于
corePoolSize? - 是 → 尝试创建一个新的核心线程来执行这个任务。
- 调用
addWorker(command, true),参数true表示创建核心线程。 - 创建成功,任务就直接交给新线程处理,方法返回,结束。
(注意:如果创建核心线程失败,比如线程池状态变化了,需要继续处理。)
第四步:线程池正在运行且队列能放进去?
if (isRunning(c) && workQueue.offer(command)) {
- 如果线程池状态是 RUNNING(正常运行中),并且
- 成功把任务加入到工作队列
workQueue。
✅ 说明:
- 没必要新建线程。
- 任务会被已有线程从队列取出执行。
第五步:二次校验(防止入队后,线程池关闭)
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
- 任务刚加进队列,线程池可能在瞬间被关闭了(比如被 shutdown() 调用了)。
- 所以需要再次检查线程池状态。
- 如果线程池不是运行状态了,而且能把刚加进去的任务移除成功 → 直接拒绝任务。
- 如果线程池是空的(worker数为 0)→ 创建一个线程去保证至少有线程执行队列里的任务。
第六步:队列放不下,扩充线程(扩展线程)
如果队列满了(offer 失败)怎么办?
else if (!addWorker(command, false))
reject(command);
- 不能直接丢弃!
- 继续尝试创建新的线程来执行这个任务。
- 调用
addWorker(command, false),false参数表示创建的是非核心线程(maximumPoolSize 范围内)。 - 如果加线程也失败(线程池满了或者关闭了) → 调用拒绝策略
reject(command)。
【总结一句话版】
execute() 的核心流程是:先看能不能加核心线程 ➔ 加不了就入队列 ➔ 入不了队列就加扩展线程 ➔ 还不行就拒绝任务。
这也是面试常问的线程池执行顺序口诀:
核心线程 ➔ 工作队列 ➔ 非核心线程 ➔ 拒绝处理
(拓展)面试补充:addWorker() 里面干了啥?
如果面试官追问:“addWorker内部是怎么保证线程安全的?”
简单回答就可以:
- addWorker() 内部用了一把重入锁(
mainLock)保护 Worker 的添加。 - 先检查线程池状态合法。
- 然后真正启动一个新的 Worker 线程(封装 Runnable,执行 runTask)。
- 线程安全、状态检查都在里面做到了。
更多详细内容请关注其他相关文章!