Java 线程池(ThreadPoolExecutor)源码分析
一、整体结构
scss
复制代码
ThreadPoolExecutor
├── 核心参数:corePoolSize、maximumPoolSize、workQueue、keepAliveTime、threadFactory、handler
├── 状态:ctl (AtomicInteger,高3位状态 + 低29位线程数)
├── 工作线程:Worker(继承 AQS,实现 Runnable)
└── 任务队列:BlockingQueue<Runnable>
二、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:线程数已满,尝试放入队列
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);
return;
}
// 步骤3:队列满,尝试创建非核心线程
if (!addWorker(command, false))
reject(command); // 步骤4:创建失败,执行拒绝策略
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 1 |
workerCountOf(c) < corePoolSize |
当前工作线程数小于核心线程数 |
核心线程是常驻的,优先用核心线程执行,减少线程创建开销 |
| 1.1 |
addWorker(command, true) |
创建核心线程并执行该任务 |
核心线程不足时立即创建,firstTask 直接交给新线程执行,无需入队 |
| 2 |
workQueue.offer(command) |
线程数已满,尝试把任务放入队列 |
核心线程已满时先入队等待,队列有界时可控制背压,避免无限制创建线程 |
| 2.1 |
isRunning(recheck) |
再次检查池是否还在运行,防止并发下状态变化 |
入队与取任务存在并发,可能在此期间调用了 shutdown,需 double-check 避免向已关闭池提交 |
| 2.2 |
workerCountOf(recheck) == 0 |
若没有工作线程,补一个空任务 Worker,保证队列任务能被消费 |
可能所有 Worker 已退出,但队列还有任务,需补 Worker 否则任务永远无法执行 |
| 3 |
addWorker(command, false) |
队列已满,尝试创建非核心线程执行任务 |
队列满说明负载高,临时扩容到 maximumPoolSize 以应对峰值 |
| 4 |
reject(command) |
线程数已达 maximumPoolSize,执行拒绝策略 |
池已满,按 RejectedExecutionHandler 策略处理(抛异常、丢弃、调用者执行等) |
三、addWorker():添加工作线程
java
复制代码
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 步骤1:检查线程池状态
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
// 步骤2:检查线程数是否超限
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
if (runStateOf(c) != rs)
continue retry;
}
}
// 步骤3:创建 Worker 并加入 workers 集合
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 {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 步骤4:启动线程
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 1 |
rs >= SHUTDOWN 判断 |
若已 SHUTDOWN/STOP/TIDYING/TERMINATED,一般不再接受新任务 |
关闭后不应再创建新 Worker,避免资源浪费和状态混乱 |
| 1.1 |
特殊:SHUTDOWN && firstTask==null && !workQueue.isEmpty() |
允许添加「空任务」Worker 去消费队列剩余任务 |
shutdown 后不再接新任务,但需把队列中已有任务执行完,空 Worker 会通过 getTask 取队列任务 |
| 2 |
wc >= corePoolSize 或 maximumPoolSize |
核心/非核心线程数已达上限,不再创建 |
防止超过配置的线程数,保证资源可控 |
| 2.1 |
compareAndIncrementWorkerCount(c) |
CAS 增加工作线程计数,避免并发超限 |
多线程并发 execute 时,CAS 保证 workerCount 准确,防止超卖 |
| 3 |
new Worker(firstTask)、workers.add(w) |
创建 Worker,加入 workers 集合 |
Worker 封装线程与任务,workers 用于 shutdown 时遍历中断 |
| 4 |
t.start() |
启动 Worker 内部线程,进入 runWorker() |
线程启动后进入循环取任务、执行任务的流程 |
四、Worker:工作线程封装
java
复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread; // 实际执行任务的线程
Runnable firstTask; // 创建时分配的第一个任务(可为 null)
Worker(Runnable firstTask) {
setState(-1); // 初始状态,防止被中断
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); // 把自己作为 Runnable
}
public void run() {
runWorker(this); // 委托给 runWorker
}
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 1 |
setState(-1) |
初始 AQS 状态为 -1 |
防止 Worker 刚创建时被误判为可中断,runWorker 中 unlock 后才允许中断 |
| 2 |
getThreadFactory().newThread(this) |
用线程工厂创建线程,this 作为 Runnable |
统一线程创建方式,Worker 的 run 会调用 runWorker,形成执行入口 |
| 3 |
继承 AQS |
实现简单锁 |
区分空闲(lock 前)与工作中(lock 后),shutdown 时只中断空闲 Worker,避免打断正在执行的任务 |
五、runWorker():工作线程主循环
java
复制代码
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
// 步骤1:循环取任务(firstTask 或 getTask())
while (task != null || (task = getTask()) != null) {
w.lock();
// 步骤2:检查是否需要中断(shutdown 等)
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task); // 钩子
try {
task.run(); // 步骤3:执行任务
} finally {
afterExecute(task, thrown); // 钩子
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); // 步骤4:Worker 退出处理
}
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 1 |
task = w.firstTask 或 getTask() |
优先执行创建时传入的 firstTask,之后从队列取任务 |
firstTask 是 addWorker 时传入的,直接执行可少一次入队出队;之后从队列取任务实现复用 |
| 2 |
状态检查 + 中断 |
若池已 STOP 或当前线程被中断,确保线程被中断,以便快速退出 |
STOP 时要尽快结束,中断可让阻塞在 getTask 的线程及时退出 |
| 3 |
task.run() |
直接执行任务(不是 start(),复用当前线程) |
Worker 线程就是工作线程,直接 run 在当前线程执行,无需再起新线程 |
| 4 |
processWorkerExit() |
线程退出时:减少 worker 计数、必要时补充新 Worker、尝试终止线程池 |
保证 workerCount 正确,SHUTDOWN 时若队列非空可补 Worker,最终尝试转为 TERMINATED |
六、getTask():从队列取任务
java
复制代码
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 步骤1:池已关闭且队列空,直接返回 null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount(c);
return null;
}
int wc = workerCountOf(c);
// 步骤2:是否允许超时回收(非核心 或 允许核心超时)
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 步骤3:阻塞取任务(带超时或不带超时)
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 1 |
`rs >= SHUTDOWN && (rs >= STOP |
|
workQueue.isEmpty())` |
| 2 |
`timed = allowCoreThreadTimeOut |
|
wc > corePoolSize` |
| 2.1 |
wc > maximumPoolSize 或 (timed && timedOut) |
线程数超限或上次取任务超时,且满足回收条件时,减少 worker 并返回 null |
扩容后负载下降需缩容;超时取到 null 表示空闲过久,可回收以释放资源 |
| 3 |
poll(keepAliveTime) / take() |
超时取或阻塞取,超时返回 null 会触发 timedOut = true,下次循环可能回收线程 |
核心线程用 take 永久阻塞保持存活;非核心用 poll 超时,超时后 timedOut 为 true 可能触发回收 |
七、整体执行流程
scss
复制代码
execute(task)
│
├─ 线程数 < corePoolSize ──→ addWorker(task, true) ──→ 新建核心线程执行
│
├─ 线程数 ≥ corePoolSize
│ │
│ ├─ workQueue.offer(task) 成功 ──→ 任务入队
│ │ │
│ │ └─ workerCount == 0 ──→ addWorker(null, false) 补一个空 Worker
│ │
│ └─ 队列满 ──→ addWorker(task, false) ──→ 新建非核心线程执行
│ │
│ └─ 失败 ──→ reject(task)
│
└─ Worker 启动 ──→ runWorker()
│
├─ 执行 firstTask
│
└─ 循环 getTask()
│
├─ take() / poll() 取到任务 ──→ task.run()
│
└─ 返回 null(超时或池关闭)──→ processWorkerExit()
八、状态与 ctl
java
复制代码
// ctl = 高3位状态 + 低29位线程数
RUNNING = -1 << 29 // 接受新任务、处理队列
SHUTDOWN = 0 << 29 // 不再接受新任务,继续处理队列
STOP = 1 << 29 // 不再接受新任务,不处理队列,中断正在执行的任务
TIDYING = 2 << 29 // 所有任务已结束,workerCount=0
TERMINATED = 3 << 29 // terminated() 已执行
九、小结
| 环节 |
作用 |
| execute() |
决定:新建核心线程 / 入队 / 新建非核心线程 / 拒绝 |
| addWorker() |
创建 Worker、加入 workers、启动线程 |
| Worker |
封装线程和 firstTask,实现 Runnable |
| runWorker() |
循环取任务并执行,退出时调用 processWorkerExit |
| getTask() |
从队列取任务,控制超时回收和池关闭时的退出 |