从源码解析keepAliveTime生效机制
在ThreadPoolExecutor中,keepAliveTime的空闲线程回收逻辑集中在**getTask()方法(约320行)和任务队列的阻塞操作**中。以下是关键源码解析:
一、回收触发点:getTask()方法
1 | private Runnable getTask() { |
二、keepAliveTime生效四步曲
步骤1:设置超时控制模式 (timed标志)
1 | boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; |
- 当满足以下任一条件时,线程进入超时控制模式:
- 当前是非核心线程(
wc > corePoolSize) - 启用了核心线程超时(
allowCoreThreadTimeOut=true)
- 当前是非核心线程(
步骤2:执行超时等待 (workQueue.poll())
1 | // 如果queue里面任务为0,会等待keepAliveTime纳秒,超时无任务,线程会被回收 |
- 调用
BlockingQueue的poll方法,参数为:keepAliveTime:用户设置的存活时间TimeUnit.NANOSECONDS:纳秒级精度
- 阻塞行为:最多等待
keepAliveTime时间,期间:- 若有新任务入队 → 立即返回任务对象
- 若超时无任务 → 返回
null
步骤3:标记超时状态 (timedOut=true)
1 | if (r != null) |
- 当
poll()返回null时,设置timedOut=true - 此标记将在下一轮循环中触发回收判断
步骤4:触发回收条件 (timed && timedOut)
1 | if ((wc > maximumPoolSize || (timed && timedOut)) // 条件A |
- 条件A(任一满足):
timed && timedOut:当前是超时控制线程且已超时 ★- 线程数超过最大值(非常规情况)
- 条件B(任一满足):
wc > 1:至少还有1个活跃线程workQueue.isEmpty():任务队列为空
- 同时满足A和B → 通过CAS减少线程计数 → 返回
null
三、线程回收执行流
当getTask()返回null后:
1 | final void runWorker(Worker w) { |
在processWorkerExit()中:
1 | private void processWorkerExit(Worker w, boolean completedAbruptly) { |
四、核心源码设计亮点
双条件保障
条件A + 条件B防止过度回收:- 避免回收最后一个有任务的线程(当
!workQueue.isEmpty()时) - 避免回收唯一存活的线程(当
wc == 1时)
- 避免回收最后一个有任务的线程(当
纳秒级精度控制
TimeUnit.NANOSECONDS确保高精度超时:1
2
3
4
5// 底层实现(LinkedBlockingQueue为例)
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout); // 转换为纳秒
// ... [精确到纳秒的等待] ...
}无锁化判断
通过compareAndDecrementWorkerCount(c)实现原子计数更新,避免同步开销。
五、线程回收全流程图示
1 | sequenceDiagram |
六、关键结论
生效位置:
keepAliveTime在workQueue.poll(keepAliveTime, NANOSECONDS)调用中生效超时判定:
当poll()方法在指定时间内未获取任务返回null时,触发超时标记回收执行:
超时标记(timedOut=true)结合线程状态判断,导致getTask()返回null,最终触发线程退出精度保障:
使用TimeUnit.NANOSECONDS确保时间精度,避免毫秒级误差
通过这种设计,线程池实现了精准的空闲线程回收,在资源利用率和响应速度之间取得平衡。
