96SEO 2026-08-01 19:37 1
其实,
你是否经常在阅读《ThreadPoolExecutor》源码后只记住了几个参数。却仍然对内部运行方式一头雾水?下面从源码层面逐步拆解任务流转、Worker 的设计、锁的选择还有异常处理,让你真正“啃透”线程池。

痛点:提交一个任务后到底是新建线程、放队列还是走拒绝策略?老实说,很多人只知道大概流程,却不清楚每一步的判定细节。
先把整体流程过一下。按理说,
一个 Task 提交进来线程池先看当前 Worker 数量有没有超过 corePoolSize。没超的话,直接创建一个新 Worker。把这个 Task 作为它的 firstTask 去执行。超了就把 Task 丢进阻塞队列排队;如果队列也满了再看能否创建新 Worker,仍不行则执行拒绝策略。
对应的 execute 实现如下:
public void execute {
if
throw new NullPointerException;int c = ctl.get;// Worker 数量
以上只是粗略的运行方式,下面继续深入。
痛点:看到 ThreadPoolExecutor 的实现代码,却不知道实际负责取任务并执行的到底是哪一块?
Worker 是线程池里的主要角色。可以把它想象成流水线上的工人。负责不断从传送带上取任务、执行,接下来再取下一个。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable {
final Thread thread;怎么说呢,// 实际执行任务的线程
Runnable firstTask;// 初始任务
volatile long completedTasks;// 完成任务数统计
Worker {
setState;// 初始化状态为 -1,抑制中断
this.firstTask = firstTask;this.thread = getThreadFactory.newThread;// 把 Worker 本身作为 Runnable 传入
}
public void run {
runWorker;}
// ...
}
Worker 类是 ThreadPoolExecutor 的内部类,实现了 Runnable 接口。在构造方法里把自己(This) 作为 Runnable 交给 ThreadFactory`newThread` 创建实际的线程。当线程启动后执行的是 Worker.run,而该方法又委托给外部类的 runWorker——这是理解后续逻辑的关键。
痛点: 看到 AddWorker 方法却不明白它到底做了哪些原子操作、什么时候会返回 false。
AddWorker 负责完成以下几件事:
Worker;wor kers;
private boolean addWorker {
retry的观点是,for {
int c = ctl.get;说起来,int rs = runStateOf;// 计算线程池运行状态
if (rs>= SHUTDOWN &&
))
return false;for {
int wc = workerCountOf;if (wc>= CAPACITY ||
wc>= )
return false;if )
break retry;不过,c = ctl.get;// Re-read ctl
if!= rs)
continue retry;// state changed,restart outer loop
// else CAS failed due to workerCount change;retry inner loop
}
}
boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {
w = new Worker;final Thread t = w.thread;if {
final ReentrantLock mainLock = this.mainLock;mainLock.lock;try {
int rs = runStateOf);if ) {
if )
throw new IllegalThreadStateException;workers.add,int s = workers.size;if
largestPoolSize = s;workerAdded = true;}
} finally {
mainLock.unlock;}
if {
t.start;// 启动内部线程
workerStarted = true;}
}
} finally {
if
addWorkerFailed;// 清理工作:移除、计数、尝试终止等
}
return workerStarted;}
a)为何多处与 S HUTDOWN 比较?**
S HUTDOWN 是线程池的一种状态,其余状态包括 RUNNING、STOP、TIDYING、TERMINATED。只有当池处于 RUNNING 或者正处于 SHUTDOWN 且还有未处理的队列任务且新增的是没有初始任务的 worker 时才允许添加。
b)
痛点: "firstTask" 与循环取任务之间到底有什么关联?为什么要用 while 循环?"
The key is that a Worker itself is a task whose job is to repeatedly fetch and run user‑submitted tasks.
public void run { // 被 Thread.start 调用 runWorker;} final void runWorker { Thread wt = Thread.currentThread;Runnable task = w.firstTask;w.firstTask = null;不过,w.unlock,// allow interrupts boolean completedAbruptly = true;try { while )!= null) { // 主要循环 w.lock;// 防止 idle 中断 try { beforeExecute;Throwable thrown = null;try { task.run;} // 真正执 行使用者代码 catch{ thrown=x;throw x,话说回来,} catch{ thrown=x;throw x,} catch{ thrown=x;throw new Error;} finally { afterExecute; } } finally { task = null;w.completedTasks++;w.unlock,} } completedAbruptly = false;按理说,} finally { processWorkerExit;怎么说呢,} }
private Runnable getTask { boolean timedOut = false;// 上一次 poll 是否超时 for { int c = ctl.get;int rs = runStateOf;if (rs>= SHUTDOWN && )) { decrementWorkerCount;return null,} int wc = workerCountOf;boolean timed = allowCoreThreadTimeOut || wc> corePoolSize;if ((wc> maximumPoolSize || ) && )) { if ) return null;continue,} try { Runnable r = timed?workQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS) : workQueue.take;if return r,说起来,timedOut = true;// poll 超时未拿到任务 } catch { timedOut = false;} } }
Pain point: "为什么不直接用 ReentrantLock,而要自己写一把锁?"
java
// 简化说明:lock -> state 从 -1 改为 0。unlock -> state 恢复 -1。
The Javadoc explicitly says:
text
While we could use a concurrent set of some sort。it turns out to be generally preferable to use a lock.
The “lock” prevents multiple threads from simultaneously interrupting idle workers during shutdown and simplifies bookkeeping such as updating `largestPo olSiz e`.
java
final ReentrantLock mainLock=this.mainLock;mainLock.lock;try{ ,}finally{ mainLock.unlock;老实说,}
注意 **lock 放在 try 前面**:如果 lock 本身抛异常且进入 finally。会导致 IllegalMonitorStateException,因为当前线程并非持有锁。
⟨ ⟩ • 按理说,#1 异常在线程内部捕获并上报至 afterExecute
java
try{
task.run;按理说,// 使用者业务代码
}catch{
thrown=x;throw x,}catch{
thrown=x;throw x,}catch{
thrown=x;throw new Error;}finally{
afterExecute;// Hook,可自定义日志/统计等
}
completedAbruptly 为 true → 表示该 worker 因异常提前退出;随后 processWor kerExit 会 decrement worker count 并 补充新 worker。
completedAbruptly 为 false → 正常从 queue 中取不到新任务而结束循环,此时不会补充。
对于 execute 提交方式。如果业务代码未捕获异常,它会沿着上述方法最终由 Thread.dispatchUncaughtException 分发给当前 thread 的 UncaughtExceptionHandler。说起来,
对于 submit 提交方式。业务代码被包装成 FutureTask。即使 call 方法抛出异常,也会被捕获并保存至 FutureResult。随后调用者通过 future.get 获取时会重新抛出 ExecutionException。怎么说呢,
java
Future
:
future.get 或检查 Future 状态来获取。| 步骤 | 主要概念 | 常见痛点 | 对策 |
|---|---|---|---|
| 提交 task | 判断 core/queue/maximum | “到底走哪条分支?” | 看 corePo olSiz e 比较 |
| 创建 Worker | 原子递增 + 锁保护 | “addW orker 为什么会返回 false?” | 检查 pool 状态 && 边界条件 |
| 运行循环 | while | “为何需要 while 循环?” | 实现“线程复用”,不断从 queue 拉取 |
| 空闲回收 | getTask 超时 && decrement | “keepAliveTime 真正控制啥?” | 带 timeout 的 poll 决定是否回收 |
| 异常处理 | 捕获→afterExecute→processExit | “异常会导致整个 pool 死掉吗?” | 异常仅影响单个 worker,pool 会自动补充 |
| 锁设计 | AQS+mainLock | “为何不用 ReentrantLock?” | 防止工作者在执行期间被误中断或重入 |
通过上述层层剖析,你应该已经能够回答以下典型面试题:
掌握这些细节。你就能真正“啃透” Java 线程池的内部实现,不再只停留在参数层面的记忆。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback