前言
本文深度剖析JDK 17线程池(ThreadPoolExecutor
)源码实现,揭示其高效处理百万级并发任务的核心设计。通过解析ctl
原子状态控制、Worker
执行单元双角色机制、三级缓冲任务调度流程(核心线程→队列→非核心线程)及优雅关闭状态机,展现Java并发大师Doug Lea的工程智慧。结合时序图与设计模式分析,深入探讨线程复用、动态扩缩容和资源管控等关键技术,并指出ThreadLocal污染等典型陷阱的解决方案,为构建高并发系统提供底层原理支撑。
一、核心设计思想
JDK线程池(java.util.concurrent.ThreadPoolExecutor
)基于生产者-消费者模型实现,核心设计原则:
- 线程复用:避免频繁创建/销毁线程的开销
- 资源管控:通过核心/最大线程数控制并发资源
- 任务排队:阻塞队列缓冲任务请求
- 拒绝策略:过载保护机制
值得借鉴的设计及意图推断:
- 状态压缩设计 :将状态和线程数合并为单个原子整型(
ctl
),避免多字段更新的竞态条件 - Worker封装:将线程和任务绑定,实现任务执行单元和锁控制的统一
- 钩子方法 :提供
beforeExecute/afterExecute
扩展点,支持监控等横切关注点
二、核心数据结构
1. 状态控制字段 ctl
java
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
-
32位整型:高3位存储线程池状态,低29位存储工作线程数
-
状态定义 :
javaprivate static final int RUNNING = -1 << COUNT_BITS; // 111 private static final int SHUTDOWN = 0 << COUNT_BITS; // 000 private static final int STOP = 1 << COUNT_BITS; // 001 private static final int TIDYING = 2 << COUNT_BITS; // 010 private static final int TERMINATED = 3 << COUNT_BITS; // 011
设计模式:状态模式(State Pattern)通过状态流转实现生命周期管理
2. 工作线程容器
java
private final HashSet<Worker> workers = new HashSet<>();
-
Worker:核心工作单元,封装线程和任务
-
线程安全 :所有访问通过ReentrantLock同步
javaprivate final ReentrantLock mainLock = new ReentrantLock();
三、任务执行流程分析
1. execute(Runnable command)
方法
java
public void execute(Runnable command) {
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) // 1. 创建核心线程
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command)) // 2. 加入队列
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false); // 3. 创建非核心线程
}
else if (!addWorker(command, false)) // 4. 创建非核心线程
reject(command); // 5. 拒绝策略
}
执行时序图:
设计意图:通过三级缓冲(核心线程→队列→非核心线程)实现资源弹性分配
四、Worker核心实现
1. Worker类结构
java
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread; // 实际执行线程
Runnable firstTask; // 初始任务
Worker(Runnable firstTask) {
setState(-1); // 禁止中断直到runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this); // 委托给线程池方法
}
// 实现简单的不可重入锁
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
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;
}
}
2. 任务执行循环 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(); // 获取Worker锁
// 中断处理逻辑...
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); // 清理工作线程
}
}
设计模式 :模板方法模式(Template Method)通过beforeExecute/afterExecute
提供扩展点
五、任务获取机制 getTask()
java
private Runnable getTask() {
boolean timedOut = false; // 上次poll是否超时
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 检查状态:SHUTDOWN+空队列 或 STOP状态
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 是否允许超时(核心线程可超时 或 线程数>corePoolSize)
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;
}
}
}
设计意图:通过超时机制实现空闲线程回收,动态调整线程池大小
六、线程回收机制
1. 超时回收
- 当
getTask()
返回null时触发线程退出 - keepAliveTime:非核心线程空闲存活时间
- allowCoreThreadTimeOut:核心线程是否允许超时
2. 异常回收
java
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // 异常退出
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w); // 从集合移除
} finally {
mainLock.unlock();
}
tryTerminate(); // 尝试终止线程池
// 维持最小线程数
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return;
}
addWorker(null, false); // 补充新Worker
}
}
设计意图:确保线程池具备自愈能力,在异常退出时自动补充新线程
七、关闭流程分析
1. shutdown()
平滑关闭
java
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
advanceRunState(SHUTDOWN); // 更新状态
interruptIdleWorkers(); // 中断空闲线程
onShutdown(); // 钩子方法
} finally {
mainLock.unlock();
}
tryTerminate();
}
2. shutdownNow()
立即关闭
java
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
advanceRunState(STOP); // 强制STOP状态
interruptWorkers(); // 中断所有工作线程
tasks = drainQueue(); // 排出队列任务
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
设计意图:提供两种关闭模式,满足不同场景的关闭需求
八、拒绝策略实现
内置四种拒绝策略均实现RejectedExecutionHandler
:
java
// 1. 默认策略:抛出异常
public static class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException();
}
}
// 2. 调用者运行策略
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run(); // 在调用线程执行
}
}
}
// 3. 丢弃策略
public static class DiscardPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
}
// 4. 丢弃最老策略
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 丢弃队列头任务
e.execute(r); // 重试执行
}
}
}
设计模式:策略模式(Strategy Pattern)允许灵活替换拒绝策略
九、关键设计亮点
-
状态压缩设计:
- 32位ctl字段同时编码状态和线程数
- 避免多字段更新的原子性问题
-
Worker双角色设计:
- 既是任务执行单元(Runnable)
- 又是同步控制单元(继承AQS)
-
优雅关闭机制:
- SHUTDOWN模式:继续处理队列任务
- STOP模式:立即中断所有线程
-
动态线程调整:
- 根据负载自动扩缩容
- 核心线程可配置超时
-
异常处理机制:
- 任务异常通过afterExecute暴露
- 工作线程异常自动补充新线程
十、线程池使用陷阱
1. ThreadLocal污染问题
问题现象:
java
// 用户身份上下文
static ThreadLocal<User> currentUser = new ThreadLocal<>();
executor.execute(() -> {
currentUser.set(loadUser()); // 任务1设置用户A
doSomeWork();
// 未清理ThreadLocal
});
executor.execute(() -> {
// 可能读取到用户A的数据!
User user = currentUser.get();
});
解决方案:
java
executor.execute(() -> {
try {
currentUser.set(loadUser());
doSomeWork();
} finally {
currentUser.remove(); // 强制清理
}
});
2. 死锁陷阱
java
// 同一线程池提交相互依赖任务
executor.submit(taskA); // taskA等待taskB结果
executor.submit(taskB); // taskB等待taskA结果
解决方案 :使用不同线程池或ForkJoinPool
3. 资源泄漏陷阱
java
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(() -> { ... });
// 忘记shutdown()导致线程无法回收
解决方案:
java
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
pool.execute(task);
} // 自动关闭(JDK21+)
4. 异常吞噬陷阱
java
executor.execute(() -> {
throw new RuntimeException("被吞没!");
});
// 主线程无法捕获异常
解决方案:
java
// 方案1:重写afterExecute
protected void afterExecute(Runnable r, Throwable t) {
if (t != null) logger.error("Uncaught exception", t);
}
// 方案2:使用Future
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
handle(e.getCause());
}
十一、最佳实践建议
-
队列选择:
- 短任务:
SynchronousQueue
(直接传递) - 长任务:
LinkedBlockingQueue
(无界缓冲) - 优先级任务:
PriorityBlockingQueue
- 短任务:
-
参数配置示例:
javanew ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), // corePoolSize Runtime.getRuntime().availableProcessors() * 2, // maxPoolSize 60L, TimeUnit.SECONDS, // keepAliveTime new LinkedBlockingQueue<>(1000), // 有界队列 new CustomThreadFactory(), new CustomRejectPolicy());
-
监控扩展:
javapublic class MonitorThreadPool extends ThreadPoolExecutor { // 任务开始/结束监控 protected void beforeExecute(Thread t, Runnable r) { monitor.recordStart(r, t); } protected void afterExecute(Runnable r, Throwable t) { monitor.recordEnd(r, t); } // 线程创建/销毁监控 protected void terminated() { monitor.poolShutdown(); } }
总结
Java线程池的执行流程本质上是生产者-消费者模型的优化实现,其核心设计思想是通过三级缓冲实现资源弹性分配:
- 核心线程处理 :当新任务提交时,线程池优先检查当前工作线程数是否小于
corePoolSize
。若是,则立即创建新工作线程处理该任务。 - 任务入队 :如果核心线程已满,线程池尝试将任务放入阻塞队列(如
LinkedBlockingQueue
)。入队成功后会二次检查线程池状态。 - 非核心线程处理 :当队列已满且工作线程数小于
maximumPoolSize
时,创建非核心线程处理任务。 - 拒绝策略 :当队列满且线程数达到
maximumPoolSize
后,新提交的任务将触发拒绝策略(如抛出RejectedExecutionException
)。
工作线程通过循环从队列获取任务执行,空闲超时(keepAliveTime
)的非核心线程会被自动回收