1. 为什么需要拆解ThreadPoolExecutor源码
在Java并发编程领域,ThreadPoolExecutor就像一位经验丰富的餐厅经理。想象一下,当大量顾客(任务)涌入餐厅时,经理需要决定:开多少张桌子(线程)接待顾客、如何安排顾客入座(任务排队)、什么时候拒绝新顾客(拒绝策略)。这些决策直接影响着餐厅的运营效率和顾客体验。
我曾在生产环境遇到过线程池配置不当导致的服务雪崩——任务堆积耗尽内存,而线程池还在不断创建新线程。通过阅读源码才发现,我们对corePoolSize和maximumPoolSize的理解存在根本性误区。这种教训让我意识到,仅靠API文档和教程远远不够,必须深入源码层面理解其运作机制。
ThreadPoolExecutor的核心价值在于它实现了任务执行与线程管理的完美解耦。它通过七个关键参数(corePoolSize、maximumPoolSize、keepAliveTime等)提供了精细的线程控制策略,这些参数之间的交互逻辑在源码中体现得淋漓尽致。比如当提交新任务时,实际执行顺序是:核心线程->任务队列->非核心线程->拒绝策略,这个流程在execute()方法中有着精妙的实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. execute()方法的三层防御体系
2.1 第一层:核心线程拦截
当调用execute(Runnable command)时,首先会检查当前工作线程数workerCount:
java复制if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
这段代码揭示了一个关键特性:即使任务队列未满,只要工作线程数小于corePoolSize,就会优先创建新线程。这解释了为什么设置corePoolSize=0时,任务会先进入队列而不是立即创建线程。
addWorker方法内部通过CAS操作保证线程安全,其核心逻辑包括:
- 使用AtomicInteger的ctl字段同时维护线程状态和数量
- 创建Worker对象包装任务和线程
- 通过ThreadFactory创建实际线程
2.2 第二层:任务队列缓冲
当核心线程满载时,任务会被offer到阻塞队列:
java复制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);
}
这里有个精妙的设计:二次检查线程池状态。因为offer操作是非阻塞的,可能在入队期间线程池被关闭。如果发生这种情况,会尝试将任务移出队列并执行拒绝策略。
特别值得注意的是最后那个else if条件:当工作线程数为0时,会创建一个空任务worker。这是为了保证即使corePoolSize=0,也有线程能处理队列中的任务。
2.3 第三层:非核心线程应急
当队列已满时,会尝试创建非核心线程:
java复制else if (!addWorker(command, false))
reject(command);
这里的addWorker第二个参数为false,表示使用maximumPoolSize作为上限。如果连非核心线程也无法创建(通常因为达到maximumPoolSize),就会触发拒绝策略。
3. Worker对象的生命周期管理
3.1 Worker的类结构玄机
Worker类继承AQS并实现Runnable,这种设计实现了两个目的:
- 通过AQS实现不可重入锁,用于任务执行时的线程安全控制
- 作为Runnable本身,其run方法会调用runWorker
java复制private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // 禁止中断直到runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
// 省略锁相关方法
}
构造方法中setState(-1)是个巧妙的设计:防止线程启动前被中断。只有在runWorker开始时才会将state置为0,允许中断。
3.2 runWorker的循环逻辑
runWorker方法体现了线程复用的核心思想:
java复制final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
这个循环包含几个关键点:
- 优先执行firstTask,之后通过getTask()从队列获取
- 每次执行任务前获取worker锁,保证任务执行期间不被中断
- 提供了beforeExecute/afterExecute钩子方法
- 异常处理确保worker能正常退出
3.3 getTask的任务获取策略
getTask()方法决定了线程何时退出:
java复制private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, SHUTDOWN) && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
这里有几个值得注意的实现细节:
- 当线程数超过corePoolSize时,会使用poll(keepAliveTime)有限等待
- 核心线程默认使用take()无限等待,除非设置allowCoreThreadTimeOut
- 线程退出条件判断非常严谨,避免过早销毁线程
4. 状态控制与优雅关闭
4.1 五种状态的位运算艺术
ThreadPoolExecutor使用AtomicInteger的ctl字段同时维护:
- 线程池状态(runState):高3位
- 工作线程数(workerCount):低29位
java复制private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 状态常量
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
这种位运算设计实现了状态和数量的原子性操作。比如判断状态:
java复制private static boolean runStateAtLeast(int c, int s) {
return c < s;
}
4.2 shutdown与shutdownNow的区别
shutdown()方法触发平缓关闭:
java复制public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // 钩子方法
} finally {
mainLock.unlock();
}
tryTerminate();
}
而shutdownNow()则更激进:
java复制public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
关键区别在于:
- shutdown将状态设为SHUTDOWN,shutdownNow设为STOP
- shutdown只中断空闲线程,shutdownNow中断所有线程
- shutdownNow会返回未执行的任务列表
4.3 终止状态转换流程
终止过程遵循严格的顺序:
RUNNING -> SHUTDOWN -> [队列和池为空] -> TIDYING -> TERMINATED
或者
(RUNNING or SHUTDOWN) -> STOP -> TIDYING -> TERMINATED
tryTerminate()方法实现了这个状态转换:
java复制final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && !workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
这个转换过程确保了terminated()钩子方法只会在所有任务完成且工作线程退出后执行一次。
