基于:JDK 17(java.util.concurrent.ThreadPoolExecutor,JDK 1.5 引入,8~17 核心逻辑一致,虚拟线程 JDK 21 另文)
本文从源码主线(execute → addWorker → runWorker → getTask → 关闭流程)讲透线程池"状态机 + Worker + 队列"三件事,再落到面试与生产都绕不开的参数调优:核心线程数怎么算、队列多大、何时触发拒绝,附 2 个可运行 Demo 复现"core → queue → max → reject"的提交顺序与线程回收、超时回收机制,最后给出生产模板与高频问答。
目录
- 核心理论
- [1.1 一句话定位](#1.1 一句话定位)
- [1.2 它解决什么问题](#1.2 它解决什么问题)
- [1.3 线程池全景定位表](#1.3 线程池全景定位表)
- [1.4 执行顺序总纲](#1.4 执行顺序总纲)
- 设计思想与核心机制
- [2.1 状态打包 ctl 与生命周期状态机](#2.1 状态打包 ctl 与生命周期状态机)
- [2.2 任务提交链路 execute](#2.2 任务提交链路 execute)
- [2.3 线程的诞生 addWorker](#2.3 线程的诞生 addWorker)
- [2.4 线程的一生 runWorker](#2.4 线程的一生 runWorker)
- [2.5 线程的回收 getTask](#2.5 线程的回收 getTask)
- [2.6 Worker 与 AQS 独占锁](#2.6 Worker 与 AQS 独占锁)
- [2.7 阻塞队列选型](#2.7 阻塞队列选型)
- [2.8 四种拒绝策略](#2.8 四种拒绝策略)
- [2.9 关闭流程 shutdown 家族](#2.9 关闭流程 shutdown 家族)
- [2.10 扩展钩子与 ScheduledThreadPoolExecutor](#2.10 扩展钩子与 ScheduledThreadPoolExecutor)
- 主线流程剖析
- [3.1 提交与执行完整链路](#3.1 提交与执行完整链路)
- [3.2 线程回收决策流程](#3.2 线程回收决策流程)
- [3.3 关闭状态机流转](#3.3 关闭状态机流转)
- [3.4 一次带回收的完整时序](#3.4 一次带回收的完整时序)
- 类关系图
- [4.1 继承体系全景](#4.1 继承体系全景)
- [4.2 内部对象关系](#4.2 内部对象关系)
- 参数调优实战
- [5.1 七大参数角色与调优影响](#5.1 七大参数角色与调优影响)
- [5.2 核心线程数估算公式](#5.2 核心线程数估算公式)
- [5.3 队列容量与峰值兜底](#5.3 队列容量与峰值兜底)
- [5.4 拒绝策略与业务选型](#5.4 拒绝策略与业务选型)
- [5.5 线程工厂与异常兜底](#5.5 线程工厂与异常兜底)
- [5.6 动态调参思路](#5.6 动态调参思路)
- [5.7 可运行 Demo 1 提交顺序实证](#5.7 可运行 Demo 1 提交顺序实证)
- [5.8 可运行 Demo 2 超时回收与动态调整](#5.8 可运行 Demo 2 超时回收与动态调整)
- [5.9 生产环境模板代码](#5.9 生产环境模板代码)
- [扩展点与常见问题 FAQ](#扩展点与常见问题 FAQ)
- [6.1 高频面试问答](#6.1 高频面试问答)
- [6.2 源码细节点辨析](#6.2 源码细节点辨析)
- [6.3 总结与学习路径](#6.3 总结与学习路径)
1. 核心理论 ★★★
1.1 一句话定位
关键认知 :ThreadPoolExecutor = 一个打包了"运行状态 + 线程数"的 AtomicInteger(ctl)+ 一份线程集合(workers)+ 一条任务队列(workQueue) 。
它的全部行为可以浓缩成一句话:核心线程干活 → 干不完进队列排队 → 队列满了再扩线程到 max → 还扛不住就按拒绝策略处理。
java
public class ThreadPoolExecutor extends AbstractExecutorService {
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// ctl 高 3 位 = runState(线程池生命周期),低 29 位 = workerCount(当前线程数)
private final BlockingQueue<Runnable> workQueue; // 任务缓冲队列
private final HashSet<Worker> workers = new HashSet<>(); // 线程集合(需 mainLock 保护)
private int corePoolSize; // 核心线程数
private int maximumPoolSize; // 最大线程数
private long keepAliveTime; // 空闲线程存活时间
private ThreadFactory threadFactory; // 线程工厂
private RejectedExecutionHandler handler; // 拒绝策略
}
1.2 它解决什么问题
| 裸用线程的问题 | 线程池的解法 |
|---|---|
| 每任务建线程:创建/销毁开销大(系统调用、栈 1MB+、GC 压力) | 线程复用:跑完任务不销毁,回池继续接活 |
| 无界并发:1 万请求开 1 万线程,CPU 空转、内存耗尽 | 池化限流:core/max 双上限 + 队列缓冲 |
| 突发流量无缓冲,直接压垮系统 | 队列削峰:瞬时任务先排队,平滑消费 |
| 无生命周期管理:停机时线程泄漏、任务丢 | 状态机 + 关闭协议:shutdown/awaitTermination 优雅停机 |
| 无法观测 | 内置指标:getPoolSize/getActiveCount/getQueue().size() 等 |
1.3 线程池全景定位表
| 工厂方法 | 线程数 | 队列 | 特点与风险 |
|---|---|---|---|
Executors.newFixedThreadPool(n) |
core=max=n | LinkedBlockingQueue 无界 | 线程数恒定;⚠️ 队列无限积压 → 内存 OOM、任务无限延迟,max 形同虚设 |
Executors.newCachedThreadPool() |
core=0,max=Integer.MAX | SynchronousQueue | 空闲 60s 回收;⚠️ 突发流量线程暴涨(上限 21 亿)→ 耗尽资源 |
Executors.newSingleThreadExecutor() |
1 | 无界 | 保证顺序;⚠️ 无界队列同上;异常退出自动补线程 |
Executors.newScheduledThreadPool(n) |
core=n | DelayedWorkQueue | 定时/周期任务;本质是 ScheduledThreadPoolExecutor |
Executors.newWorkStealingPool() |
并行度参数 | 双端队列 | ⚠️ 返回的是 ForkJoinPool,不是 ThreadPoolExecutor(工作窃取模型) |
⚠️ 阿里巴巴 Java 开发手册(强制):线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式显式配置------规避无界队列 OOM 与线程数暴涨两类事故。
1.4 执行顺序总纲
线程池的扩容逻辑是严格分段的 ,一段走完才进入下一段:
① workerCount < corePoolSize → 新建核心线程立即执行 → ② 否则任务入队 → ③ 队列满 → 新建非核心线程到 maximumPoolSize → ④ 满员且队列满 → 拒绝策略
#mermaid-svg-cVhBpfHbJzV0YZFJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cVhBpfHbJzV0YZFJ .error-icon{fill:#552222;}#mermaid-svg-cVhBpfHbJzV0YZFJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cVhBpfHbJzV0YZFJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .marker.cross{stroke:#333333;}#mermaid-svg-cVhBpfHbJzV0YZFJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cVhBpfHbJzV0YZFJ p{margin:0;}#mermaid-svg-cVhBpfHbJzV0YZFJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .cluster-label text{fill:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .cluster-label span{color:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .cluster-label span p{background-color:transparent;}#mermaid-svg-cVhBpfHbJzV0YZFJ .label text,#mermaid-svg-cVhBpfHbJzV0YZFJ span{fill:#333;color:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .node rect,#mermaid-svg-cVhBpfHbJzV0YZFJ .node circle,#mermaid-svg-cVhBpfHbJzV0YZFJ .node ellipse,#mermaid-svg-cVhBpfHbJzV0YZFJ .node polygon,#mermaid-svg-cVhBpfHbJzV0YZFJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .rough-node .label text,#mermaid-svg-cVhBpfHbJzV0YZFJ .node .label text,#mermaid-svg-cVhBpfHbJzV0YZFJ .image-shape .label,#mermaid-svg-cVhBpfHbJzV0YZFJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-cVhBpfHbJzV0YZFJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .rough-node .label,#mermaid-svg-cVhBpfHbJzV0YZFJ .node .label,#mermaid-svg-cVhBpfHbJzV0YZFJ .image-shape .label,#mermaid-svg-cVhBpfHbJzV0YZFJ .icon-shape .label{text-align:center;}#mermaid-svg-cVhBpfHbJzV0YZFJ .node.clickable{cursor:pointer;}#mermaid-svg-cVhBpfHbJzV0YZFJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .arrowheadPath{fill:#333333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cVhBpfHbJzV0YZFJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cVhBpfHbJzV0YZFJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cVhBpfHbJzV0YZFJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cVhBpfHbJzV0YZFJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .cluster text{fill:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ .cluster span{color:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cVhBpfHbJzV0YZFJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cVhBpfHbJzV0YZFJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-cVhBpfHbJzV0YZFJ .icon-shape,#mermaid-svg-cVhBpfHbJzV0YZFJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cVhBpfHbJzV0YZFJ .icon-shape p,#mermaid-svg-cVhBpfHbJzV0YZFJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cVhBpfHbJzV0YZFJ .icon-shape .label rect,#mermaid-svg-cVhBpfHbJzV0YZFJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cVhBpfHbJzV0YZFJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cVhBpfHbJzV0YZFJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cVhBpfHbJzV0YZFJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
新任务提交
workerCount < core ?
新建核心线程执行
队列不满 ?
任务进队列排队
workerCount < max ?
新建非核心线程执行
触发拒绝策略
两个反直觉但必须记住的点:
- 只要 workerCount 没到 core,新任务就新开线程------即使此时有核心线程空闲(空闲线程阻塞在队列 take 上,无代价;线程数会先补齐到 core);
- 队列没满之前绝不会扩到 max------max 线程是"队列也扛不住"的最后手段,不是"线程不够就扩"。
2. 设计思想与核心机制 ★★★
2.1 状态打包 ctl 与生命周期状态机 ★
线程池需要原子地维护两个量:运行状态 runState 与 线程数 workerCount 。如果拆成两个 volatile 字段,修改其一必须加锁才能保证整体一致;AQS 团队的做法是打包进一个 int,一次 CAS 同时改两个量:
java
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; // 29
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 低 29 位全 1 = 536870911
private static int runStateOf(int c) { return c & ~CAPACITY; } // 取高 3 位
private static int workerCountOf(int c) { return c & CAPACITY; } // 取低 29 位
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 高 3 位取值(数值上 RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED,可直接比较)
private static final int RUNNING = -1 << COUNT_BITS; // 接受新任务 + 处理队列任务
private static final int SHUTDOWN = 0 << COUNT_BITS; // 拒绝新任务,队列任务继续
private static final int STOP = 1 << COUNT_BITS; // 拒绝新任务 + 丢弃队列 + 中断所有 worker
private static final int TIDYING = 2 << COUNT_BITS; // 线程全部终止,即将执行钩子
private static final int TERMINATED = 3 << COUNT_BITS; // 钩子执行完,终态
关键认知 :因为数值单调递增,源码里
runStateAtLeast(c, STOP)、runStateLessThan(c, STOP)都用比较 而非等值判断------RUNNING 是负数(-1<<29),所以"RUNNING"要特判runStateOf(c) == RUNNING或< SHUTDOWN。把状态设计成可比较序,是这套状态机最优雅的地方。
状态流转:
#mermaid-svg-Mdk4Hfz5dZTfh7BO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .error-icon{fill:#552222;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .marker.cross{stroke:#333333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO p{margin:0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-Mdk4Hfz5dZTfh7BO g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Mdk4Hfz5dZTfh7BO g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Mdk4Hfz5dZTfh7BO .edgeLabel .label text{fill:#333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .label div .edgeLabel{color:#333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO #statediagram-barbEnd{fill:#333333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .cluster-label,#mermaid-svg-Mdk4Hfz5dZTfh7BO .nodeLabel{color:#131300;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .note-edge{stroke-dasharray:5;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-note text{fill:black;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram-note .nodeLabel{color:black;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagram .edgeLabel{color:red;}#mermaid-svg-Mdk4Hfz5dZTfh7BO #dependencyStart,#mermaid-svg-Mdk4Hfz5dZTfh7BO #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-Mdk4Hfz5dZTfh7BO .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Mdk4Hfz5dZTfh7BO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 构造完成即 RUNNING
shutdown()(中断空闲线程,队列任务跑完)
shutdownNow()(清空队列,中断全部线程)
shutdownNow()(同左)
队列已空 且 workerCount == 0
workerCount == 0
terminated() 钩子执行完毕
RUNNING
SHUTDOWN
STOP
TIDYING
TERMINATED
为什么 shutdown 只中断"空闲"线程? 让正在跑任务的线程自然执行完,队列剩余任务也由活着/空闲的 worker 继续消费直到清空,是"优雅"二字的全部含义。状态机推进由 tryTerminate() 负责(worker 退出、shutdown 时都会尝试,条件满足就把 SHUTDOWN/STOP 推到 TIDYING 并执行 terminated 钩子)。
2.2 任务提交链路 execute
java
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
// 第 1 段:核心线程没满 → 直接新开核心线程跑
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) return;
c = ctl.get(); // 加失败(池已关闭)→ 重新读状态往下走
}
// 第 2 段:池在 RUNNING 且队列能塞下 → 入队
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get(); // ★ 二次检查
if (!isRunning(recheck) && remove(command)) // 入队后池被 shutdown → 移除并拒绝
reject(command);
else if (workerCountOf(recheck) == 0) // 池活着但没有线程可消费(core=0 场景)
addWorker(null, false);
}
// 第 3 段:队列也满了 → 尝试扩非核心线程,扩不动(达 max 或已关闭)→ 拒绝
else if (!addWorker(command, false))
reject(command);
}
为什么有 recheck 二次检查? 竞态窗口:任务 offer 入队成功后、判断完成前,线程池恰好被 shutdown------若不复查,这个任务会"安静地烂在队列里"(SHUTDOWN 下 worker 只会在队列清空后退出,任务本身还是会跑......严格说 SHUTDOWN 会跑完队列任务,真正的风险是入队后立刻被 shutdownNow 清空丢弃,且调用方以为已提交成功)。复查保证提交语义:要么任务已入队且池仍可消费,要么立刻移除交给拒绝策略,绝不悬空。
2.3 线程的诞生 addWorker
java
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 只在两种状态放行新线程:RUNNING,或 SHUTDOWN 且 firstTask == null(补空转线程去消费队列)
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null)) return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false;
if (compareAndIncrementWorkerCount(c)) break retry; // CAS 抢占"名额"
c = ctl.get();
if (runStateOf(c) != rs) continue retry; // 期间状态变了 → 外层重来
}
}
// ------ 名额到手,下面创建线程(可能失败需回滚)------
boolean workerStarted = false;
Worker w = null;
try {
w = new Worker(firstTask); // 内部通过 threadFactory 创建 Thread(还没 start)
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); // workers HashSet 必须加锁保护
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) throw new IllegalThreadStateException();
workers.add(w); // 登记进线程集合
int s = workers.size();
if (s > largestPoolSize) largestPoolSize = s;
t.start(); // ★ 启动线程 → 触发 Worker.run() → runWorker()
workerStarted = true;
}
} finally { mainLock.unlock(); }
}
} finally {
if (!workerStarted) {
addWorkerFailed(w); // 回滚:workerCount-1、移除 Worker、尝试终止
}
}
return workerStarted;
}
三个细节:名额与创建分离 (先 CAS 抢线程名额,后建线程,失败回滚------不会超发线程);状态双重校验 (CAS 前后都要看 runState,防止"刚关闭的池又冒出线程");mainLock 只保护 workers 集合,不参与任务执行路径(任务执行完全无锁)。
2.4 线程的一生 runWorker
Worker 启动后进入 runWorker,这是每个线程从生到死的执行循环:
java
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; // 第一个任务直接开工
w.firstTask = null;
w.unlock(); // state -1 → 0,放行"中断许可"(见 2.6)
boolean completedAbruptly = true; // 是否"异常退出"
try {
while (task != null || (task = getTask()) != null) { // 没活干就去 getTask 阻塞等
w.lock(); // 执行期间上"忙碌锁"
// 池进入 STOP 才补中断;否则吞掉中断位让任务不受打扰(Thread.interrupted 会清除标志)
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
&& !wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task); // 钩子① 任务执行前
try {
task.run(); // ★ 真正执行任务(注意是 run 不是 start)
afterExecute(task, null); // 钩子② 任务执行后
} catch (Throwable ex) {
afterExecute(task, ex); // 异常也通知钩子
throw ex; // 重新抛出 → 跳出循环 → 线程"暴毙"退出
}
} finally {
task = null;
w.completedTasks++; // 该 worker 完成数 +1
w.unlock(); // 解锁,恢复"可中断"状态
}
}
completedAbruptly = false; // 正常退出:getTask 返回 null
} finally {
processWorkerExit(w, completedAbruptly); // 无论生死都要走善后(见 3.2)
}
}
关键认知 :任务里抛异常(RuntimeException)→
afterExecute仍会被调用(带异常参数)→ 异常沿栈抛出 → 该线程直接退出 ,不会继续循环!随后processWorkerExit发现是异常退出(completedAbruptly=true)会自动补一个新线程------所以 execute 提交的任务异常不会让池"少人",但异常信息只进线程的 stderr(默认不记录日志),这是"execute 丢异常"问题的根源。
2.5 线程的回收 getTask
Worker 没有任务时不会退出,而是阻塞在 getTask;只有当 getTask 返回 null 时线程才走向消亡:
java
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 条件①:池已关闭且没有活可干(STOP 直接退;SHUTDOWN 需队列空)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount(); // 让出名额
return null; // → runWorker 退出 → 线程结束
}
int wc = workerCountOf(c);
// 是否"计时回收":非核心线程总是计时;核心线程只有 allowCoreThreadTimeOut(true) 才计时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 条件②:超时没等到任务(timedOut)或线程数超上限 → 让出名额退出
// (wc > 1 || workQueue.isEmpty() 保证:队列还有活时永远至少留 1 个线程)
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c)) return null;
continue;
}
try {
// 核心区分点:带超时的 poll vs 无限期 take
Runnable r = timed
? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) // 等 keepAliveTime 就超时
: workQueue.take(); // 无限等
if (r != null) return r;
timedOut = true; // poll 超时 → 下一轮判定回收
} catch (InterruptedException retry) {
timedOut = false; // 被 interrupt(如 shutdown)→ 清超时标记重试判定
}
}
}
线程退出只有三条路 :① 池进入 STOP / SHUTDOWN 且队列空;② 超时回收(非核心 or 允许超时的核心);③ 线程数被 setMaximumPoolSize 调低到当前值以下。退出时用 CAS 递减 workerCount,队列里还有任务时保底留 1 个线程 (wc > 1 || workQueue.isEmpty() 的奥义)。
2.6 Worker 与 AQS 独占锁 ★
Worker 是线程池里"一个线程"的化身,它继承了 \[AQS原理知识点] 的独占锁,把锁当作忙碌标志:
java
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread; // 真正干活的线程(由 threadFactory 创建)
Runnable firstTask; // 首个任务(null = 纯"备胎线程",从队列取活)
volatile long completedTasks; // 本 worker 累计完成任务数
Worker(Runnable firstTask) {
setState(-1); // ★ 初始 state = -1:禁止被中断(线程还没 start)
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() { runWorker(this); } // 线程启动入口
// ------ 用 AQS 实现"不可重入独占锁":state 0→1 成功 = 抢到"忙碌锁" ------
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; }
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null); setState(0); return true;
}
public void lock() { acquire(1); } // runWorker 每轮任务前调用
public void unlock() { release(1); }
public boolean tryLock() { return tryAcquire(1); } // 非阻塞试探
void interruptIfStarted() { // shutdownNow 用:只中断已启动的线程
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted())
t.interrupt();
}
}
为什么 Worker 要继承 AQS? 官方注释给的理由:shutdown 要"只中断空闲 worker",需要一个不可重入 的互斥量当忙碌标志------tryLock() 成功 = 该线程此刻没在执行任务(阻塞在 getTask)→ 可以安全 interrupt;失败 = 正在跑任务(runWorker 持锁中)→ 跳过。用 synchronized 或可重入锁做不了"非阻塞试探拿锁",AQS 的 tryAcquire 天然支持。而 setState(-1) 的玄机是:线程刚 new 出来还没 start 时,state=-1 会让 interruptIfStarted(getState() >= 0 判断)拒绝中断它,防止"线程还没跑起来就被 shutdownNow 中断成死胎"。
2.7 阻塞队列选型
队列是 core 与 max 之间的"蓄水池",选型直接决定线程池行为:
| 队列 | 结构 | 有界性 | 行为特征 | 搭配建议 |
|---|---|---|---|---|
LinkedBlockingQueue |
链表 | 默认无界(可传容量) | 吞吐高,无界时 max 失效 | Fixed/Single 默认队列 ⚠️ 慎用无界 |
ArrayBlockingQueue |
数组 | 必须设容量 | 公平模式可配(构造器 fair) | 需要严格有界 + 公平排队 |
SynchronousQueue |
无缓冲 | 视作 0 | offer 必须刚好有线程 take 才成功,否则任务"溢出"去扩线程 | CachedThreadPool 默认:任务即来即走,绝不积压 |
DelayQueue |
堆 | 无界 | 按延迟出队 | ScheduledThreadPoolExecutor 内部(DelayedWorkQueue) |
PriorityBlockingQueue |
堆 | 无界 | 按优先级出队 | 少数"重要任务先跑"场景 |
2.8 四种拒绝策略
当 execute 三段全走完(已达 max 且队列满),或池已关闭时触发:
| 策略 | 行为 | 适用 |
|---|---|---|
AbortPolicy(默认) |
直接抛 RejectedExecutionException |
明确失败信号,调用方可感知 |
CallerRunsPolicy |
调用者线程自己执行该任务 | 天然背压:调用方被拖慢 → 上游减速(削峰第一选择) |
DiscardPolicy |
静默丢弃,无任何提示 | ⚠️ 不推荐裸用 |
DiscardOldestPolicy |
丢弃队列队头最老任务,再尝试提交 | 允许丢旧任务保新的场景(如实时性任务) |
⚠️ 所有策略都可能被"池已关闭"触发,DiscardOldestPolicy 与 CallerRunsPolicy 内部都先判断
!executor.isShutdown();生产上无论选哪种,都建议包装一层:记录拒绝现场(任务、队列水位、线程数)告警。
2.9 关闭流程 shutdown 家族
| 方法 | 状态目标 | 行为 | 队列任务 | 正在执行的任务 |
|---|---|---|---|---|
shutdown() |
SHUTDOWN | 拒绝新提交;中断空闲 worker | 继续执行完 | 不打断,执行完 |
shutdownNow() |
STOP | 清空队列返回;中断所有已启动 worker | 以 List 返回,不执行 | 收到中断(配合响应中断的任务) |
awaitTermination(timeout, unit) |
阻塞等待 | 配合 shutdown 使用,超时返回 false | ------ | ------ |
java
// 优雅停机标准三段式
pool.shutdown(); // ① 不再接新活
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) { // ② 等队列清空 + 线程结束(给宽限时间)
pool.shutdownNow(); // ③ 等不及了:强停并拿回未执行任务
if (!pool.awaitTermination(30, TimeUnit.SECONDS))
System.err.println("线程池未能终止,任务可能中断在途"); // ④ 终极兜底
}
2.10 扩展钩子与 ScheduledThreadPoolExecutor
三个 protected 钩子,子类可覆写(相当于给线程池加 AOP):
beforeExecute(Thread t, Runnable r):任务执行前------可做 MDC/TraceId 注入(把调用线程的上下文塞进 worker 线程);afterExecute(Runnable r, Throwable t):任务执行后------无论任务抛不抛异常都会被调用,这是捕获 execute 任务异常做日志/指标的唯一正统位置(注意:submit 的 FutureTask 异常不经过这里抛,因为 FutureTask.run 内部吞了异常);terminated():状态到 TERMINATED 前调用一次------可做池关闭后的资源清理、上报。
ScheduledThreadPoolExecutor extends ThreadPoolExecutor :覆写 execute/submit 全部转进 schedule;内部队列换成 DelayedWorkQueue(按触发时间出队);scheduleAtFixedRate(固定频率,上一次开始算)与 scheduleWithFixedDelay(固定延迟,上一次结束算)的核心差异在触发时间计算。覆写 onShutdown 逻辑保证关闭时取消并清空延迟任务。
3. 主线流程剖析 ★★★
3.1 提交与执行完整链路
#mermaid-svg-OXuefxNZ7Phvn02R{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OXuefxNZ7Phvn02R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OXuefxNZ7Phvn02R .error-icon{fill:#552222;}#mermaid-svg-OXuefxNZ7Phvn02R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OXuefxNZ7Phvn02R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OXuefxNZ7Phvn02R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OXuefxNZ7Phvn02R .marker.cross{stroke:#333333;}#mermaid-svg-OXuefxNZ7Phvn02R svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OXuefxNZ7Phvn02R p{margin:0;}#mermaid-svg-OXuefxNZ7Phvn02R .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OXuefxNZ7Phvn02R .cluster-label text{fill:#333;}#mermaid-svg-OXuefxNZ7Phvn02R .cluster-label span{color:#333;}#mermaid-svg-OXuefxNZ7Phvn02R .cluster-label span p{background-color:transparent;}#mermaid-svg-OXuefxNZ7Phvn02R .label text,#mermaid-svg-OXuefxNZ7Phvn02R span{fill:#333;color:#333;}#mermaid-svg-OXuefxNZ7Phvn02R .node rect,#mermaid-svg-OXuefxNZ7Phvn02R .node circle,#mermaid-svg-OXuefxNZ7Phvn02R .node ellipse,#mermaid-svg-OXuefxNZ7Phvn02R .node polygon,#mermaid-svg-OXuefxNZ7Phvn02R .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OXuefxNZ7Phvn02R .rough-node .label text,#mermaid-svg-OXuefxNZ7Phvn02R .node .label text,#mermaid-svg-OXuefxNZ7Phvn02R .image-shape .label,#mermaid-svg-OXuefxNZ7Phvn02R .icon-shape .label{text-anchor:middle;}#mermaid-svg-OXuefxNZ7Phvn02R .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OXuefxNZ7Phvn02R .rough-node .label,#mermaid-svg-OXuefxNZ7Phvn02R .node .label,#mermaid-svg-OXuefxNZ7Phvn02R .image-shape .label,#mermaid-svg-OXuefxNZ7Phvn02R .icon-shape .label{text-align:center;}#mermaid-svg-OXuefxNZ7Phvn02R .node.clickable{cursor:pointer;}#mermaid-svg-OXuefxNZ7Phvn02R .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OXuefxNZ7Phvn02R .arrowheadPath{fill:#333333;}#mermaid-svg-OXuefxNZ7Phvn02R .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OXuefxNZ7Phvn02R .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OXuefxNZ7Phvn02R .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OXuefxNZ7Phvn02R .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OXuefxNZ7Phvn02R .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OXuefxNZ7Phvn02R .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OXuefxNZ7Phvn02R .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OXuefxNZ7Phvn02R .cluster text{fill:#333;}#mermaid-svg-OXuefxNZ7Phvn02R .cluster span{color:#333;}#mermaid-svg-OXuefxNZ7Phvn02R div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OXuefxNZ7Phvn02R .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OXuefxNZ7Phvn02R rect.text{fill:none;stroke-width:0;}#mermaid-svg-OXuefxNZ7Phvn02R .icon-shape,#mermaid-svg-OXuefxNZ7Phvn02R .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OXuefxNZ7Phvn02R .icon-shape p,#mermaid-svg-OXuefxNZ7Phvn02R .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OXuefxNZ7Phvn02R .icon-shape .label rect,#mermaid-svg-OXuefxNZ7Phvn02R .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OXuefxNZ7Phvn02R .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OXuefxNZ7Phvn02R .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OXuefxNZ7Phvn02R :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
池已关闭
池正常 但 workerCount==0
否 队列满
失败 达 max 或已关闭
抛异常
正常
有 getTask 取到
getTask 阻塞等待
超时回收
队列 take 到任务
调用 execute(task)
workerCount < corePoolSize ?
addWorker(task, core=true)
CAS 抢线程名额 → 创建 Worker → t.start()
runWorker 循环
池 RUNNING 且队列 offer 成功 ?
任务入队,recheck 二次检查
remove 出队 → reject 拒绝策略
补一个空转线程 addWorker(null, false)
addWorker(task, core=false) 扩非核心线程
reject → 拒绝策略
w.lock 打忙碌标记 → beforeExecute
task.run() 执行任务
afterExecute(task, ex) → 异常上抛 → 线程退出 → processWorkerExit 补员
afterExecute(task, null) → w.unlock
还有任务?
keepAlive 超时 / 池关闭?
CAS 递减名额 → runWorker 退出 → processWorkerExit
编号版主线(提交一个任务到线程池的完整旅程):
execute先看核心线程数是否满 (workerCountOf(c) < corePoolSize)------不满就 addWorker 建核心线程,任务作为firstTask直接开跑;- 满了则
workQueue.offer(command)入队(非阻塞,队列满立刻返回 false); - 入队成功后 recheck :若期间池被关闭,
remove掉任务走拒绝策略;若池活着但没有任何线程(core=0 的池),补一个空转线程(addWorker(null, false))保证队列会被消费; - 队列满则 addWorker(command, false) 扩非核心线程(上限 maximumPoolSize,核心参数 false);
- 扩不动(已满 max)→ 拒绝策略兜底;
- Worker 线程启动进
runWorker:先执行 firstTask,之后循环getTask()从队列取任务,take/poll阻塞等待; - 每执行一个任务前后打
w.lock()/w.unlock()忙碌标记(配合 2.6 的中断策略); - 队列取不到任务且超时(或池关闭)→ getTask 返回 null → 线程退出 →
processWorkerExit善后:从 workers 移除、累加 completedTaskCount、异常退出时补新线程 、tryTerminate尝试推进状态机。
3.2 线程回收决策流程
#mermaid-svg-IrweSDANc3WCTgcP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IrweSDANc3WCTgcP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IrweSDANc3WCTgcP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IrweSDANc3WCTgcP .error-icon{fill:#552222;}#mermaid-svg-IrweSDANc3WCTgcP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IrweSDANc3WCTgcP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IrweSDANc3WCTgcP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IrweSDANc3WCTgcP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IrweSDANc3WCTgcP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IrweSDANc3WCTgcP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IrweSDANc3WCTgcP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IrweSDANc3WCTgcP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IrweSDANc3WCTgcP .marker.cross{stroke:#333333;}#mermaid-svg-IrweSDANc3WCTgcP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IrweSDANc3WCTgcP p{margin:0;}#mermaid-svg-IrweSDANc3WCTgcP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IrweSDANc3WCTgcP .cluster-label text{fill:#333;}#mermaid-svg-IrweSDANc3WCTgcP .cluster-label span{color:#333;}#mermaid-svg-IrweSDANc3WCTgcP .cluster-label span p{background-color:transparent;}#mermaid-svg-IrweSDANc3WCTgcP .label text,#mermaid-svg-IrweSDANc3WCTgcP span{fill:#333;color:#333;}#mermaid-svg-IrweSDANc3WCTgcP .node rect,#mermaid-svg-IrweSDANc3WCTgcP .node circle,#mermaid-svg-IrweSDANc3WCTgcP .node ellipse,#mermaid-svg-IrweSDANc3WCTgcP .node polygon,#mermaid-svg-IrweSDANc3WCTgcP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IrweSDANc3WCTgcP .rough-node .label text,#mermaid-svg-IrweSDANc3WCTgcP .node .label text,#mermaid-svg-IrweSDANc3WCTgcP .image-shape .label,#mermaid-svg-IrweSDANc3WCTgcP .icon-shape .label{text-anchor:middle;}#mermaid-svg-IrweSDANc3WCTgcP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IrweSDANc3WCTgcP .rough-node .label,#mermaid-svg-IrweSDANc3WCTgcP .node .label,#mermaid-svg-IrweSDANc3WCTgcP .image-shape .label,#mermaid-svg-IrweSDANc3WCTgcP .icon-shape .label{text-align:center;}#mermaid-svg-IrweSDANc3WCTgcP .node.clickable{cursor:pointer;}#mermaid-svg-IrweSDANc3WCTgcP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IrweSDANc3WCTgcP .arrowheadPath{fill:#333333;}#mermaid-svg-IrweSDANc3WCTgcP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IrweSDANc3WCTgcP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IrweSDANc3WCTgcP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IrweSDANc3WCTgcP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IrweSDANc3WCTgcP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IrweSDANc3WCTgcP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IrweSDANc3WCTgcP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IrweSDANc3WCTgcP .cluster text{fill:#333;}#mermaid-svg-IrweSDANc3WCTgcP .cluster span{color:#333;}#mermaid-svg-IrweSDANc3WCTgcP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IrweSDANc3WCTgcP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IrweSDANc3WCTgcP rect.text{fill:none;stroke-width:0;}#mermaid-svg-IrweSDANc3WCTgcP .icon-shape,#mermaid-svg-IrweSDANc3WCTgcP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IrweSDANc3WCTgcP .icon-shape p,#mermaid-svg-IrweSDANc3WCTgcP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IrweSDANc3WCTgcP .icon-shape .label rect,#mermaid-svg-IrweSDANc3WCTgcP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IrweSDANc3WCTgcP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IrweSDANc3WCTgcP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IrweSDANc3WCTgcP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否 核心线程且不允许超时
是 计时线程
等到任务
超时 timedOut
否 只有我一个且队列还有活
是 可退
Worker 完成当前任务
再次 getTask()
池已 STOP 或 SHUTDOWN 且队列空 ?
CAS 名额-1 → 返回 null → 线程退出
allowCoreThreadTimeOut
或 wc > corePoolSize ?
workQueue.take() 无限期阻塞
(线程常驻)
workQueue.poll(keepAliveTime)
阻塞限时等待
返回任务 → 继续执行
wc > 1 或队列空 ?
3.3 关闭状态机流转
worker-2(空闲等队列) worker-1(执行任务中) 线程池 调用方 worker-2(空闲等队列) worker-1(执行任务中) 线程池 调用方 #mermaid-svg-o80cdsDVcrmxz0Dd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o80cdsDVcrmxz0Dd .error-icon{fill:#552222;}#mermaid-svg-o80cdsDVcrmxz0Dd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o80cdsDVcrmxz0Dd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o80cdsDVcrmxz0Dd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o80cdsDVcrmxz0Dd .marker.cross{stroke:#333333;}#mermaid-svg-o80cdsDVcrmxz0Dd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o80cdsDVcrmxz0Dd p{margin:0;}#mermaid-svg-o80cdsDVcrmxz0Dd .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o80cdsDVcrmxz0Dd text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o80cdsDVcrmxz0Dd .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-o80cdsDVcrmxz0Dd .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-o80cdsDVcrmxz0Dd #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-o80cdsDVcrmxz0Dd .sequenceNumber{fill:white;}#mermaid-svg-o80cdsDVcrmxz0Dd #sequencenumber{fill:#333;}#mermaid-svg-o80cdsDVcrmxz0Dd #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-o80cdsDVcrmxz0Dd .messageText{fill:#333;stroke:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o80cdsDVcrmxz0Dd .labelText,#mermaid-svg-o80cdsDVcrmxz0Dd .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .loopText,#mermaid-svg-o80cdsDVcrmxz0Dd .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o80cdsDVcrmxz0Dd .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-o80cdsDVcrmxz0Dd .noteText,#mermaid-svg-o80cdsDVcrmxz0Dd .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-o80cdsDVcrmxz0Dd .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o80cdsDVcrmxz0Dd .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o80cdsDVcrmxz0Dd .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o80cdsDVcrmxz0Dd .actorPopupMenu{position:absolute;}#mermaid-svg-o80cdsDVcrmxz0Dd .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-o80cdsDVcrmxz0Dd .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o80cdsDVcrmxz0Dd .actor-man circle,#mermaid-svg-o80cdsDVcrmxz0Dd line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-o80cdsDVcrmxz0Dd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 队列空 && workerCount==0 → 状态 → TIDYING → 执行 terminated() → TERMINATED alt 强制关闭 shutdown():状态 → SHUTDOWN interruptIdleWorkers:tryLock 成功才中断(W2 空闲 → 命中) getTask 被中断 → 检查状态 → SHUTDOWN 且队列空 → 退出 processWorkerExit → tryTerminate awaitTermination(60s) 返回 true shutdownNow():状态 → STOP,清空队列返回 List interruptIfStarted() 中断执行中的任务线程
3.4 一次带回收的完整时序
场景:core=2, max=4, 队列容量 2,主线程依次提交 7 个 1.5s 的任务:
任务队列(容量2) worker-4(非核心) worker-3(非核心) worker-2(核心) worker-1(核心) ThreadPoolExecutor 主线程 任务队列(容量2) worker-4(非核心) worker-3(非核心) worker-2(核心) worker-1(核心) ThreadPoolExecutor 主线程 #mermaid-svg-QlB0lyKtuQjBQZlA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QlB0lyKtuQjBQZlA .error-icon{fill:#552222;}#mermaid-svg-QlB0lyKtuQjBQZlA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QlB0lyKtuQjBQZlA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QlB0lyKtuQjBQZlA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QlB0lyKtuQjBQZlA .marker.cross{stroke:#333333;}#mermaid-svg-QlB0lyKtuQjBQZlA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QlB0lyKtuQjBQZlA p{margin:0;}#mermaid-svg-QlB0lyKtuQjBQZlA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QlB0lyKtuQjBQZlA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QlB0lyKtuQjBQZlA .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-QlB0lyKtuQjBQZlA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-QlB0lyKtuQjBQZlA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-QlB0lyKtuQjBQZlA .sequenceNumber{fill:white;}#mermaid-svg-QlB0lyKtuQjBQZlA #sequencenumber{fill:#333;}#mermaid-svg-QlB0lyKtuQjBQZlA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-QlB0lyKtuQjBQZlA .messageText{fill:#333;stroke:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QlB0lyKtuQjBQZlA .labelText,#mermaid-svg-QlB0lyKtuQjBQZlA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .loopText,#mermaid-svg-QlB0lyKtuQjBQZlA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QlB0lyKtuQjBQZlA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-QlB0lyKtuQjBQZlA .noteText,#mermaid-svg-QlB0lyKtuQjBQZlA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-QlB0lyKtuQjBQZlA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QlB0lyKtuQjBQZlA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QlB0lyKtuQjBQZlA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QlB0lyKtuQjBQZlA .actorPopupMenu{position:absolute;}#mermaid-svg-QlB0lyKtuQjBQZlA .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-QlB0lyKtuQjBQZlA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QlB0lyKtuQjBQZlA .actor-man circle,#mermaid-svg-QlB0lyKtuQjBQZlA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-QlB0lyKtuQjBQZlA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 两线程各跑 1.5s 提交任务1 1 workerCount 0<2 → addWorker(核心) 执行 2 提交任务2 3 workerCount 1<2 → addWorker(核心) 执行 4 提交任务3 5 workerCount=2 不<core → 入队(3) 6 提交任务4 7 入队(3,4)队列已满 8 提交任务5 9 队列满 → addWorker(非核心) worker-3 执行 10 提交任务6 11 队列仍满 → worker-4 执行 12 提交任务7 13 满员且队列满 → AbortPolicy 抛 RejectedExecutionException 14 1.5s 后完成 → 从队列取任务3 15 完成 → 取任务4 16 完成 → 无任务 → poll(keepAlive) 超时 → 回收退出 17
4. 类关系图 ★★★
4.1 继承体系全景
#mermaid-svg-KTfhhESd5oKJdZ6F{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KTfhhESd5oKJdZ6F .error-icon{fill:#552222;}#mermaid-svg-KTfhhESd5oKJdZ6F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KTfhhESd5oKJdZ6F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KTfhhESd5oKJdZ6F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KTfhhESd5oKJdZ6F .marker.cross{stroke:#333333;}#mermaid-svg-KTfhhESd5oKJdZ6F svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KTfhhESd5oKJdZ6F p{margin:0;}#mermaid-svg-KTfhhESd5oKJdZ6F g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-KTfhhESd5oKJdZ6F g.classGroup text .title{font-weight:bolder;}#mermaid-svg-KTfhhESd5oKJdZ6F .cluster-label text{fill:#333;}#mermaid-svg-KTfhhESd5oKJdZ6F .cluster-label span{color:#333;}#mermaid-svg-KTfhhESd5oKJdZ6F .cluster-label span p{background-color:transparent;}#mermaid-svg-KTfhhESd5oKJdZ6F .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KTfhhESd5oKJdZ6F .cluster text{fill:#333;}#mermaid-svg-KTfhhESd5oKJdZ6F .cluster span{color:#333;}#mermaid-svg-KTfhhESd5oKJdZ6F .nodeLabel,#mermaid-svg-KTfhhESd5oKJdZ6F .edgeLabel{color:#131300;}#mermaid-svg-KTfhhESd5oKJdZ6F .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-KTfhhESd5oKJdZ6F .label text{fill:#131300;}#mermaid-svg-KTfhhESd5oKJdZ6F .labelBkg{background:#ECECFF;}#mermaid-svg-KTfhhESd5oKJdZ6F .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-KTfhhESd5oKJdZ6F .classTitle{font-weight:bolder;}#mermaid-svg-KTfhhESd5oKJdZ6F .node rect,#mermaid-svg-KTfhhESd5oKJdZ6F .node circle,#mermaid-svg-KTfhhESd5oKJdZ6F .node ellipse,#mermaid-svg-KTfhhESd5oKJdZ6F .node polygon,#mermaid-svg-KTfhhESd5oKJdZ6F .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KTfhhESd5oKJdZ6F .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F g.clickable{cursor:pointer;}#mermaid-svg-KTfhhESd5oKJdZ6F g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-KTfhhESd5oKJdZ6F g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-KTfhhESd5oKJdZ6F .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-KTfhhESd5oKJdZ6F .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-KTfhhESd5oKJdZ6F .dashed-line{stroke-dasharray:3;}#mermaid-svg-KTfhhESd5oKJdZ6F .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-KTfhhESd5oKJdZ6F #compositionStart,#mermaid-svg-KTfhhESd5oKJdZ6F .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #compositionEnd,#mermaid-svg-KTfhhESd5oKJdZ6F .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #dependencyStart,#mermaid-svg-KTfhhESd5oKJdZ6F .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #dependencyStart,#mermaid-svg-KTfhhESd5oKJdZ6F .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #extensionStart,#mermaid-svg-KTfhhESd5oKJdZ6F .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #extensionEnd,#mermaid-svg-KTfhhESd5oKJdZ6F .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #aggregationStart,#mermaid-svg-KTfhhESd5oKJdZ6F .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #aggregationEnd,#mermaid-svg-KTfhhESd5oKJdZ6F .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #lollipopStart,#mermaid-svg-KTfhhESd5oKJdZ6F .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F #lollipopEnd,#mermaid-svg-KTfhhESd5oKJdZ6F .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KTfhhESd5oKJdZ6F .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-KTfhhESd5oKJdZ6F .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KTfhhESd5oKJdZ6F .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KTfhhESd5oKJdZ6F .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KTfhhESd5oKJdZ6F :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 继承(不可重入独占锁)
实现
返回内部包装类
工作窃取(注意不是 TPE)
<<interface>>
Executor
+execute(Runnable)
<<interface>>
ExecutorService
+shutdown()
+shutdownNow()
+submit(Callable)
+invokeAll(Collection)
AbstractExecutorService
+submit(Runnable)
+invokeAll(Collection)
ThreadPoolExecutor
-AtomicInteger ctl
-BlockingQueue workQueue
-HashSet workers
+setCorePoolSize(int)
+setMaximumPoolSize(int)
+setKeepAliveTime(long, TimeUnit)
+allowCoreThreadTimeOut(boolean)
#beforeExecute(Thread, Runnable)
#afterExecute(Runnable, Throwable)
#terminated()
ScheduledThreadPoolExecutor
+schedule(Runnable, long, TimeUnit)
+scheduleAtFixedRate(Runnable, long, long, TimeUnit)
Worker
+Thread thread
+Runnable firstTask
-volatile long completedTasks
AbstractQueuedSynchronizer
Runnable
<<工厂工具类>>
Executors
+newFixedThreadPool(int)
+newCachedThreadPool()
+newSingleThreadExecutor()
+newScheduledThreadPool(int)
+newWorkStealingPool()
ForkJoinPool
4.2 内部对象关系
#mermaid-svg-UaO0pm4BLHy97itY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UaO0pm4BLHy97itY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UaO0pm4BLHy97itY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UaO0pm4BLHy97itY .error-icon{fill:#552222;}#mermaid-svg-UaO0pm4BLHy97itY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UaO0pm4BLHy97itY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UaO0pm4BLHy97itY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UaO0pm4BLHy97itY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UaO0pm4BLHy97itY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UaO0pm4BLHy97itY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UaO0pm4BLHy97itY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UaO0pm4BLHy97itY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UaO0pm4BLHy97itY .marker.cross{stroke:#333333;}#mermaid-svg-UaO0pm4BLHy97itY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UaO0pm4BLHy97itY p{margin:0;}#mermaid-svg-UaO0pm4BLHy97itY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UaO0pm4BLHy97itY .cluster-label text{fill:#333;}#mermaid-svg-UaO0pm4BLHy97itY .cluster-label span{color:#333;}#mermaid-svg-UaO0pm4BLHy97itY .cluster-label span p{background-color:transparent;}#mermaid-svg-UaO0pm4BLHy97itY .label text,#mermaid-svg-UaO0pm4BLHy97itY span{fill:#333;color:#333;}#mermaid-svg-UaO0pm4BLHy97itY .node rect,#mermaid-svg-UaO0pm4BLHy97itY .node circle,#mermaid-svg-UaO0pm4BLHy97itY .node ellipse,#mermaid-svg-UaO0pm4BLHy97itY .node polygon,#mermaid-svg-UaO0pm4BLHy97itY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UaO0pm4BLHy97itY .rough-node .label text,#mermaid-svg-UaO0pm4BLHy97itY .node .label text,#mermaid-svg-UaO0pm4BLHy97itY .image-shape .label,#mermaid-svg-UaO0pm4BLHy97itY .icon-shape .label{text-anchor:middle;}#mermaid-svg-UaO0pm4BLHy97itY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UaO0pm4BLHy97itY .rough-node .label,#mermaid-svg-UaO0pm4BLHy97itY .node .label,#mermaid-svg-UaO0pm4BLHy97itY .image-shape .label,#mermaid-svg-UaO0pm4BLHy97itY .icon-shape .label{text-align:center;}#mermaid-svg-UaO0pm4BLHy97itY .node.clickable{cursor:pointer;}#mermaid-svg-UaO0pm4BLHy97itY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UaO0pm4BLHy97itY .arrowheadPath{fill:#333333;}#mermaid-svg-UaO0pm4BLHy97itY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UaO0pm4BLHy97itY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UaO0pm4BLHy97itY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UaO0pm4BLHy97itY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UaO0pm4BLHy97itY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UaO0pm4BLHy97itY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UaO0pm4BLHy97itY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UaO0pm4BLHy97itY .cluster text{fill:#333;}#mermaid-svg-UaO0pm4BLHy97itY .cluster span{color:#333;}#mermaid-svg-UaO0pm4BLHy97itY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UaO0pm4BLHy97itY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UaO0pm4BLHy97itY rect.text{fill:none;stroke-width:0;}#mermaid-svg-UaO0pm4BLHy97itY .icon-shape,#mermaid-svg-UaO0pm4BLHy97itY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UaO0pm4BLHy97itY .icon-shape p,#mermaid-svg-UaO0pm4BLHy97itY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UaO0pm4BLHy97itY .icon-shape .label rect,#mermaid-svg-UaO0pm4BLHy97itY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UaO0pm4BLHy97itY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UaO0pm4BLHy97itY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UaO0pm4BLHy97itY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ThreadPoolExecutor
取任务
决定是否新建/回收
Worker × N
thread(真实 OS 线程)
AQS state
-1 未启动 / 0 空闲 / 1 执行中
ctl AtomicInteger
高3位 runState + 低29位 workerCount
workQueue 任务队列
workers HashSet 线程集合
mainLock 主锁
保护 workers 与状态迁移
5. 参数调优实战 ★★★
5.1 七大参数角色与调优影响
| 参数 | 语义 | 调小/调大的代价 | 运行时可否修改 |
|---|---|---|---|
corePoolSize 核心线程数 |
常驻线程池下限 | 小→吞吐受限;大→常驻资源占用 | ✅ setCorePoolSize(调大补线程、调小中断空闲核心) |
maximumPoolSize 最大线程数 |
队列满时扩到的上限 | 小→易触发拒绝;大→线程暴涨风险 | ✅ setMaximumPoolSize(调小后超出的空闲线程会被回收) |
workQueue 任务队列 |
core 与 max 之间的蓄水池 | 见 5.3 | ❌ 构造后不可换(需换队列只能重建或自研包装) |
keepAliveTime 空闲存活 |
非核心线程(或允许超时的核心)空闲多久回收 | 小→线程频繁创建销毁;大→空闲占资源 | ✅ setKeepAliveTime(非核心立即生效) |
threadFactory 线程工厂 |
线程命名、daemon、优先级 | 不命名 = 排查问题时"pool-N-thread-M"毫无业务线索 | ❌ |
handler 拒绝策略 |
满载或关闭时的兜底 | 见 5.4 | ✅ setRejectedExecutionHandler |
allowCoreThreadTimeOut 隐式参数 |
核心线程是否也可超时回收 | true 后线程数可降到 0 | ✅ allowCoreThreadTimeOut(true)(要求 keepAliveTime > 0) |
5.2 核心线程数估算公式 ★
理论基准公式(线程利用率 U 与阻塞比 W/C 决定):
线程数 = Ncpu × U × (1 + W / C)
其中 W = 平均等待(IO/锁阻塞)时间,C = 平均计算时间
工程近似:
| 任务类型 | 判据 | 经验值 |
|---|---|---|
| CPU 密集 | 几乎不阻塞(纯计算/压缩/加密) | Ncpu + 1(多 1 个补偿页缺失等停顿) |
| IO 密集 | 网络/RPC/DB/文件占大头 | Ncpu × 2 ~ Ncpu × 3 (粗粒度)或 Ncpu / (1 - 阻塞系数)(阻塞系数 0.8~0.9 时 ≈ Ncpu × 5~10) |
java
// 获取可用核数(注意容器环境:K8s 未设置 limits 时读到的可能是物理机核数!)
int ncpu = Runtime.getRuntime().availableProcessors();
⚠️ 公式只能给起点,线上必须"先压测再微调" :设置理论值 → 压测观察(CPU 利用率、队列水位、RT 分位线)→ 按 5.6 动态调整。纯 IO 任务的真实最优线程数常远超公式(如 RPC 等待 100ms、计算 1ms 时 Ncpu×100 也能扛住),因为瓶颈在 IO 不在 CPU------调优目标是"别让线程成为瓶颈,也别让 CPU/内存被打爆",而不是追求某个公式 。
容器场景注意:
availableProcessors()受 cgroup 影响(JDK 10+ 已感知容器配额),但未设 CPU limits 时仍可能虚高;可结合-XX:ActiveProcessorCount显式指定。
5.3 队列容量与峰值兜底
队列容量没有银弹公式,先想清楚两件事:
- 你允许任务最多排队多久(容忍延迟 T):队列容量 ≥ 峰值入队速率 P × 容忍延迟 T;
- 队列满之后的动作 :满了才会动 max 线程、再满才拒绝------队列越大,max 越难触发,拒绝越晚,但任务延迟与积压内存越大。
java
// 估算示例:接口峰值 500 req/s,允许单任务最多等 2s → 队列至少 1000
// 经验线:LinkedBlockingQueue 容量常取 1000~10000(视任务体量与内存),
// 而不是 Executors 的"无界"(= 无限积压)
int queueCapacity = 1000; // ≈ 峰值速率 × 容忍排队秒数
推荐组合策略(美团《Java线程池实现原理及其在美团业务中的实践》的思路):
| 水位 | 线程数取值 | 语义 |
|---|---|---|
| 平时 | core = 平均负载下稳定需要的线程数 | 常态运行成本最小 |
| 瞬时尖峰 | queue 吸收一部分(容量有限) | 削峰 |
| 持续积压 | max = 能扛住的最坏峰值(结合 CPU/内存预算) | 兜底扩线 |
| 全满 | 拒绝策略 + 告警 | 限流信号 |
5.4 拒绝策略与业务选型
| 场景 | 推荐 |
|---|---|
| 异步任务丢了也无所谓(日志、埋点) | 包装 DiscardPolicy + 打点计数 |
| 希望上游感知负载(经典 RPC/Web 场景) | CallerRunsPolicy:调用线程亲自执行 = 自然反压;或 Abort + 调用方 catch 降级 |
| 必须报错不能让调用方蒙在鼓里 | AbortPolicy(默认),配合统一异常包装 |
| 队列里旧任务可牺牲(行情推送只保留最新) | DiscardOldestPolicy |
拒绝不等于结束:生产做法通常是 handler 里记录"拒绝快照"(此刻 core/max/队列大小/活跃线程数)→ 发告警 → 再按策略执行。拒绝是系统过载的信号,不是吞任务的借口。
5.5 线程工厂与异常兜底
线程命名是排障第一生产力,自定义 ThreadFactory 两行搞定:
java
ThreadFactory bizFactory = new ThreadFactory() {
private final AtomicInteger seq = new AtomicInteger(1);
@Override public Thread newThread(Runnable r) {
Thread t = new Thread(r, "order-async-" + seq.getAndIncrement()); // 业务前缀
t.setDaemon(false); // 默认非守护,进程退出前会等待
t.setUncaughtExceptionHandler((th, e) -> // 线程级兜底日志
log.error("线程 {} 意外崩溃", th.getName(), e));
return t;
}
};
任务异常两条防线(execute 提交的任务异常默认不会打到业务日志,必须自己兜):
java
// 防线1:覆写 afterExecute(execute 与 submit 都必经,唯一正统打点位置)
@Override protected void afterExecute(Runnable r, Throwable t) {
// submit 的异常被 FutureTask 吞了,需要这样取回
if (t == null && r instanceof Future<?> f) {
try { f.get(0, TimeUnit.NANOSECONDS); }
catch (ExecutionException e) { t = e.getCause(); }
catch (Exception ignored) {}
}
if (t != null) log.error("任务执行异常", t);
}
// 防线2:业务代码自己 try-catch(最可靠,别指望框架)
5.6 动态调参思路 ★
线程池参数不需要重启就能改(这正是 ThreadPoolExecutor 比大多数自研池高级的地方),生产调优应是"运行中观察 → 运行中微调"的闭环:
| 动作 | 触发 | 内部实现 |
|---|---|---|
setCorePoolSize(n) |
调大 | 尝试 addWorker(null) 补线程直到 n |
setCorePoolSize(n) |
调小 | interruptIdleWorkers() 逐出空闲核心线程(正在跑的不动) |
setMaximumPoolSize(n) |
调小(小于当前线程数) | 超出的线程在 getTask 中被判定 wc > maximumPoolSize 自行退出 |
setKeepAliveTime(t) |
任意时刻 | 立即影响下一次 poll 超时判定 |
allowCoreThreadTimeOut(true) |
核心也想回收 | 核心线程转计时回收,线程数可降到 0 |
setRejectedExecutionHandler(h) |
调整过载行为 | 直接生效 |
配套监控四板斧(动态调参的依据):
java
// ① 内部指标快照
int poolSize = pool.getPoolSize(); // 当前线程数
int active = pool.getActiveCount(); // 正在执行任务的线程数
long queueSize = pool.getQueue().size(); // 排队任务数(核心水位指标)
long largest = pool.getLargestPoolSize(); // 历史最大线程数(峰值证据)
long taskCount = pool.getTaskCount(); // 已提交任务总数
long completed = pool.getCompletedTaskCount(); // 已完成数
double activeRate = (double) active / poolSize; // 忙碌率:连续 >0.8 且队列涨 → 调大;长期 <0.5 → 调小
② 运维层:Arthas threadpool 命令可直接查看/修改 JVM 内线程池参数(热更新场景神器);③ Spring Boot 场景暴露 actuator 线程池指标或用 ThreadPoolTaskExecutor 统一托管;④ 告警阈值:队列水位 > 80%、拒绝次数 > 0、活跃线程 == max 持续 N 分钟。
5.7 可运行 Demo 1 提交顺序实证
用日志证据复现"core → queue → max → reject"四段扩容,跑完就懂参数含义。
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TpeOrderDemo {
static final AtomicInteger seq = new AtomicInteger(0);
static long t0 = System.currentTimeMillis();
public static void main(String[] args) throws Exception {
// core=2 max=4,队列容量 2
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, 4, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2),
r -> new Thread(r, "order-worker-" + seq.getAndIncrement()),
new ThreadPoolExecutor.AbortPolicy());
Runnable job = () -> {
String name = Thread.currentThread().getName();
System.out.printf("[%4dms] %s 开始执行(队列=%d 线程数=%d)%n",
System.currentTimeMillis() - t0, name,
pool.getQueue().size(), pool.getPoolSize());
sleep(1500); // 模拟业务耗时,保持 worker 忙碌
System.out.printf("[%4dms] %s 完成%n", System.currentTimeMillis() - t0, name);
};
submitAndNote(pool, job, 1); // ① 核心1 诞生
submitAndNote(pool, job, 2); // ② 核心2 诞生
sleep(150); // 让 1、2 都在跑(1.5s 内)
submitAndNote(pool, job, 3); // ③ 核心满 → 入队(队列 1/2)
submitAndNote(pool, job, 4); // ④ 入队(队列 2/2 满)
sleep(150);
submitAndNote(pool, job, 5); // ⑤ 队列满 → 扩 worker-3(非核心)
submitAndNote(pool, job, 6); // ⑥ 队列仍满 → 扩 worker-4(非核心,达 max)
try {
submitAndNote(pool, job, 7); // ⑦ 满员+队列满 → 拒绝!
} catch (RejectedExecutionException e) {
System.out.printf("[%4dms] ❌ 任务7 被拒绝:%s%n", System.currentTimeMillis() - t0,
e.getClass().getSimpleName());
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("========= 观察结论 =========");
System.out.println("1、2 由核心线程执行;3、4 先排队(没有任何新线程出现);");
System.out.println("5、6 才触发非核心扩线到 max=4;7 在满员+队列满时被 AbortPolicy 拒绝。");
}
static void submitAndNote(ThreadPoolExecutor pool, Runnable job, int no) {
System.out.printf("[%4dms] 提交任务%d%n", System.currentTimeMillis() - t0, no);
pool.execute(job);
}
static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
}
预期输出形态(时间戳略):
text
[ 0ms] 提交任务1
[ 1ms] order-worker-1 开始执行(队列=0 线程数=1)
[ 2ms] 提交任务2
[ 3ms] order-worker-2 开始执行(队列=0 线程数=2)
[ 152ms] 提交任务3
[ 153ms] 提交任务4
[ 302ms] 提交任务5
[ 302ms] order-worker-3 开始执行(队列=2 线程数=3) ← 5 没排队,直接扩线
[ 303ms] 提交任务6
[ 303ms] order-worker-4 开始执行(队列=2 线程数=4) ← 6 扩到 max
[ 305ms] 提交任务7
[ 305ms] ❌ 任务7 被拒绝:RejectedExecutionException
[1500ms+] order-worker-1 完成 → 取走队列中的任务3 ...
5.8 可运行 Demo 2 超时回收与动态调整
演示非核心线程 keepAlive 回收、核心线程
allowCoreThreadTimeOut后也能归零、以及setMaximumPoolSize运行时缩编。
java
import java.util.concurrent.*;
public class TpeRecycleDemo {
static long t0 = System.currentTimeMillis();
static void p(String s) { System.out.printf("[%4dms] %s%n", System.currentTimeMillis() - t0, s); }
public static void main(String[] args) throws Exception {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, 3, 1, TimeUnit.SECONDS, // core=1 max=3 keepAlive=1s
new ArrayBlockingQueue<>(1)); // 有界队列容量 1 → 逼出非核心线程
pool.allowCoreThreadTimeOut(true); // ★ 核心线程也允许超时回收
for (int i = 1; i <= 3; i++) { // 三个任务 0.8s:1 进核心、2 排队、3 逼队列满扩线
pool.execute(() -> sleep(800));
sleep(60);
}
p("提交后:poolSize=" + pool.getPoolSize() + "(应为 3:1核心+2非核心)");
sleep(2000); // 任务全部完成,keepAlive=1s 已过
p("超时后:poolSize=" + pool.getPoolSize() + "(应为 0:非核心先回收,核心因 allowCoreThreadTimeOut 也回收)");
p("largestPoolSize=" + pool.getLargestPoolSize() + "(历史峰值 3,监控要点)");
// ------ 动态调参演示:重新提交任务后直接运行时缩编 ------
for (int i = 1; i <= 3; i++) { pool.execute(() -> sleep(1200)); sleep(60); }
p("二次提交后:poolSize=" + pool.getPoolSize() + "(3)");
pool.setMaximumPoolSize(1); // ★ 运行时把上限调到 1:超出的 2 个空闲线程将被回收
p("setMaximumPoolSize(1) 后立即:poolSize=" + pool.getPoolSize()
+ "(空闲非核心线程在 getTask 下一轮判定退出)");
sleep(2000);
p("两秒后:poolSize=" + pool.getPoolSize() + "(回收至 1 个核心内)");
pool.shutdownNow();
}
static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
}
预期输出 :3 → 0 → 3 →(setMaximumPoolSize(1))→ 1,完整展示超时回收与动态缩编两条回收路径。
5.9 生产环境模板代码
直接抄改的生产级配置(Spring Boot 场景可套进 ThreadPoolTaskExecutor,Bean 销毁时 @PreDestroy 自动触发优雅停机):
java
public ThreadPoolExecutor buildBizPool() {
int ncpu = Runtime.getRuntime().availableProcessors();
// 命名工厂 + 非守护 + 崩溃兜底日志
ThreadFactory factory = new ThreadFactory() {
private final AtomicInteger seq = new AtomicInteger(1);
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "biz-async-" + seq.getAndIncrement());
t.setUncaughtExceptionHandler((th, e) ->
log.error("线程池线程崩溃: {}", th.getName(), e));
return t;
}
};
ThreadPoolExecutor pool = new ThreadPoolExecutor(
ncpu * 2, // core:IO 密集先按 2×CPU 起步
ncpu * 4, // max:压测后按峰值微调
60, TimeUnit.SECONDS, // 空闲回收
new ArrayBlockingQueue<>(1000), // 有界!容量 = 峰值速率 × 容忍排队时间
factory,
new ThreadPoolExecutor.CallerRunsPolicy()); // 背压兜底
// 全参数动态化入口:监控系统/管理接口可随时改
DynamicPoolRegistry.register("biz-async", pool); // 暴露 setXxx + 指标快照
return pool;
}
// 优雅停机(Spring 用 @PreDestroy / DisposableBean / SmartLifecycle#stop 统一触发)
public void shutdownGracefully() {
pool.shutdown(); // ① 不再接新活
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
pool.shutdownNow(); // ② 宽限 30s 后强停
pool.awaitTermination(5, TimeUnit.SECONDS); // ③ 最后再等 5s
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt(); // 恢复中断位
}
}
完整方案还应包含:任务里放 TraceId/业务上下文 (beforeExecute 注入 MDC,afterExecute 清理,防止串号)、
afterExecute打点(执行时长、异常率)、队列水位告警、以及 hook 到发版平台的重建流程(大版本升级时优雅停机 + 新池预热,避免停机窗口)。
6. 扩展点与常见问题 FAQ ★★★
6.1 高频面试问答
Q1:execute 的完整处理顺序?
core 未满先开核心线程直接跑;满了入队;队列满扩非核心线程到 max;满员且队列满走拒绝策略。队列不满时绝不会新建非核心线程。
Q2:为什么 core 满了先进队列而不是直接扩 max?
平滑资源使用:队列吸收瞬时尖峰,线程数稳定在 core 附近避免频繁建/毁线程;持续积压才逐级扩到 max。若"任务来了就扩线",线程数会随流量剧烈震荡,扩线成本(创建、上下文切换、内存)反而成为瓶颈。
Q3:非核心线程怎么被回收?
getTask 里 timed = allowCoreThreadTimeOut || wc > corePoolSize:非核心线程走 workQueue.poll(keepAliveTime) 限时阻塞,超时没等到任务 → 下一轮判定 timed && timedOut → CAS 名额-1 退出。队列还有任务时保底留 1 个线程。
Q4:shutdown 与 shutdownNow 的区别?
shutdown → SHUTDOWN:拒绝新任务、只中断空闲 线程,正在执行与队列任务都跑完;shutdownNow → STOP:清空队列(返回未执行任务 List)、中断所有已启动线程,正在执行的任务收到 interrupt。配合 awaitTermination 才能确定终态。
Q5:execute 的任务抛异常会怎样?
该 worker 线程直接退出(runWorker 异常上抛 → completedAbruptly → processWorkerExit),池检测到异常退出会自动补一个新线程 ;异常本身默认不记录(可用 afterExecute 钩子统一兜底)。submit 则不同 :异常被 FutureTask 捕获,Future.get() 抛 ExecutionException,线程不退出。
Q6:核心线程数怎么定?
CPU 密集 ≈ Ncpu+1;IO 密集 ≈ Ncpu×2 起步或按 Ncpu/(1-阻塞系数);公式只给起点,必须压测 + 动态调优(5.2/5.6)。容器环境注意 CPU 配额感知问题。
Q7:为什么不建议 Executors 工厂方法?
Fixed/Single 用无界队列:任务积压不拒绝、内存可被拖垮(OOM),且 max 形同虚设;Cached 的 max 为 Integer.MAX_VALUE:突发流量线程暴涨打垮系统。阿里规约强制手写 ThreadPoolExecutor。
Q8:核心线程能回收吗?线程数能到 0 吗?
allowCoreThreadTimeOut(true)(要求 keepAliveTime>0)后核心线程同样超时回收,线程数可降到 0(队列来了任务才懒创建)。适合"白天高峰晚上闲"的场景省资源。
Q9:如何优雅停机?
shutdown → awaitTermination(宽限) → 超时 shutdownNow → 再 awaitTermination 兜底 → 恢复中断位。Spring 场景交给容器生命周期(@PreDestroy/SmartLifecycle),另可注册 JVM shutdown hook 兜底。
Q10:怎么监控线程池?
内部指标:getPoolSize/getActiveCount/getQueue().size()/getLargestPoolSize/getTaskCount/getCompletedTaskCount;活跃率 = active/poolSize(>0.8 且队列涨说明要扩容)。工具:Arthas threadpool 命令可看可改;Spring 用 actuator + ThreadPoolTaskExecutor 托管;关键告警:队列水位、拒绝次数。
Q11:单线程池里两个任务先后提交,能保证按序执行吗?
newSingleThreadExecutor 保证(1 个 worker 顺序取任务),但注意:任务里异常退出后补的线程继续执行后续任务,顺序仍保持;而"先提交的任务被拒绝后由调用者执行"(CallerRuns)会打乱顺序------细节场景需自行设计。
Q12:线程池与虚拟线程(JDK 21+)什么关系?
虚拟线程的定位是"一任务一线程、由 JVM 调度海量轻量线程",大量 IO 阻塞场景不再需要池化复用;但 ThreadPoolExecutor 仍是 ExecutorService 的标准实现、可控并发上限的通用设施(虚拟线程也可配固定调度器实现限流池化),二者互补而非替代,平台线程池代码基本不受影响。
6.2 源码细节点辨析
D1:为什么把 runState 和 workerCount 打包装进一个 AtomicInteger?
两个字段若分开,要么加锁才能保证"改状态同时改线程数"的原子性(如 shutdown 瞬间新 worker 冒出来),要么出现读不一致。打包后用 compareAndIncrementWorkerCount 等 CAS 一次原子改两量,所有快照 ctl.get() 读一次即一致。取 29 位低位的 workerCount 上限 536870911 实际不可达,无约束意义。
D2:execute 里二次检查 recheck 到底防什么?
防"offer 成功与最终判定之间线程池被 shutdown/shutdownNow 的竞态":入队后若池已关闭,任务可能永远不被执行且调用方毫不知情,所以 remove(command) 取回 → 走拒绝策略,保证每个任务都有明确归宿(执行 or 拒绝)。
D3:addWorker 的 rs == SHUTDOWN && firstTask == null 放行条件怎么理解?
SHUTDOWN 状态队列里可能还有任务,但不允许新建线程执行新任务------唯一例外是补"空转线程"(firstTask=null)去消费队列里剩余任务。这是"优雅关停不丢队列任务"的最后一块拼图(与 getTask 中 SHUTDOWN+队列空则退出的逻辑呼应)。
D4:Worker 初始 state=-1 的深意?
runWorker 第一行 w.unlock() 把 -1 置 0。在此之前 interruptIfStarted() 判断 getState() >= 0 不成立 → shutdownNow 也不会中断这个"还没 start 完"的线程;之后线程才算"可中断"。而 runWorker 每任务 w.lock() 让 AQS state 变 1,interruptIdleWorkers 的 w.tryLock() 拿不到锁 → 正在执行任务的核心不会被 shutdown 误伤。
D5:任务异常退出后补员规则的精确定义?
processWorkerExit 中:completedAbruptly=true(异常退出)→ 无条件尝试 addWorker(null, false) 补一个;正常退出 → 按 allowCoreThreadTimeOut ? 0 : corePoolSize(队列非空时保底 1)决定是否补。所以"execute 抛异常"的线程虽然死了,但池的容量机制会自愈。
D6:corePoolSize=0 的池(CachedThreadPool)任务为什么能狂飙线程?
execute 第一段 workerCount < 0 恒不成立;第二段队列 offer:SynchronousQueue 无缓冲,只有恰好有线程在 take 时才成功 ------没有空闲线程时 offer 必然失败 → 走第三段 addWorker(false) 新建线程 → 直到 max(Cached 的 max 是 Integer.MAX_VALUE)。线程完成后 poll 60s 超时回收。即:任务来多少开多少,全靠"无线程在等"自然触发扩线。
D7:SHUTDOWN 下阻塞在 take 的 worker 怎么醒来?
shutdown() 的 interruptIdleWorkers 中断空闲 worker → getTask 的 catch (InterruptedException retry) 把 timedOut 置 false → 重新循环 → rs >= SHUTDOWN && workQueue.isEmpty()(队列已被消费空)→ 名额-1 返回 null 退出;若队列还有任务则继续 take 消费------循环保证"任务没跑完线程不死"。
D8:FutureTask 在 runWorker 里的特殊性?
submit 返回的 FutureTask 在 task.run() 时内部 catch 了所有异常存入 outcome,因此 runWorker 的 catch 分支不会 触发(afterExecute 的 Throwable 参数是 null)------这也是很多人 afterExecute 里看不到 submit 任务异常的原因,需要用 future.get() 主动取回(见 5.5 防线 1 代码)。
6.3 总结与学习路径
一句话复习:
ThreadPoolExecutor 是一个用 ctl 打包状态与线程数、用 Worker(AQS 锁当忙碌标志)循环执行、用队列缓冲削峰 的可复用线程管家。扩容顺序 core → queue → max → reject 是它全部行为的钥匙;回收靠 getTask 的"限时阻塞 + 名额让渡";关闭靠状态机 SHUTDOWN/STOP + tryTerminate 的协作推进。调优没有银弹公式------先按 5.2 起步,再按监控动态调,永远让拒绝策略当你的过载报警器。
上手路径建议:先跑 Demo 1 亲眼看到四段扩容(10 分钟)→ 对照 3.1 流程图读 execute/addWorker/runWorker 源码 → 用 Demo 2 验证回收与动态调整 → 看 shutdown 家族源码确认关闭语义 → 回 6.1 自测问答。动态调参进阶参考美团工程实践《Java线程池实现原理及其在美团业务中的实践》;虚拟线程对池化的冲击另见相关笔记。