线程池(ThreadPoolExecutor)实现原理和源码细节是Java高并发面试和实战开发的重点
一、线程池核心流程图
+-----------------+
| 提交任务 | submit/execute
+-----------------+|v
+-----------------+
| 判断核心线程数 | < corePoolSize?
+-----------------+|Yes |Nov v
[创建新线程] +-----------------+| 队列是否满? |+-----------------+|No |Yesv v[入队列排队] +------------------+| 判断最大线程数 |+------------------+|No |Yesv v[创建新线程] [执行拒绝策略]
二、线程池主要环节及源码方法
1. 任务提交(execute/submit)
方法:
execute(Runnable command)
submit(Runnable/Callable)
源码片段:
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}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);}else if (!addWorker(command, false))reject(command);
}
注释速记:
- 任务先尝试用核心线程处理。
- 核心线程满则尝试入队列。
- 队列满则尝试新建非核心线程。
- 实在不行,执行拒绝策略。
口诀:
先核心,后队列;队列满,再扩容;全满员,拒绝它。
2. 核心线程判断与创建
方法:
addWorker(Runnable firstTask, boolean core)
源码片段:
private boolean addWorker(Runnable firstTask, boolean core) {retry:for (;;) {int c = ctl.get();int rs = runStateOf(c);// ...省略状态判断for (;;) {int wc = workerCountOf(c);if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))return false;if (compareAndIncrementWorkerCount(c))break retry;c = ctl.get();if (runStateOf(c) != rs)continue retry;}}// ...真正创建Worker线程
}
注释速记:
- 根据core参数决定是否用核心线程池大小。
- 用CAS增加线程计数,线程安全。
口诀:
核心先上,CAS抢位,线程安全,才创建。
3. 入队列
方法:
workQueue.offer(command)
源码片段:
if (isRunning(c) && workQueue.offer(command)) {// 入队成功后可能需要唤醒线程
}
注释速记:
- 队列没满则入队。
- 入队后如果线程都在忙,线程池不会立刻扩容。
口诀:
队列能放,直接排队。
4. 非核心线程扩容
逻辑:
- 当核心线程和队列都满时,允许创建新线程(最大线程数以内)。
源码片段:
else if (!addWorker(command, false))reject(command);
注释速记:
- 只有在核心线程和队列都满时才会扩容到最大线程数。
口诀:
满员排队,再扩容。
5. 拒绝策略
方法:
RejectedExecutionHandler.rejectedExecution(Runnable r, ThreadPoolExecutor e)
源码片段:
public static class AbortPolicy implements RejectedExecutionHandler {public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException();}
}
注释速记:
- 线程池和队列都满时,根据策略处理(抛异常/丢弃/调用者执行等)。
口诀:
全都满员,策略管。
6. 线程回收与销毁
方法:
worker.run()
- 判断空闲时间超过keepAliveTime
源码片段:
while (task != null || (task = getTask()) != null) {// 执行任务
}
注释速记:
- 非核心线程空闲时间到达后会被回收。
- 核心线程默认不会被回收(可通过allowCoreThreadTimeOut配置)。
口诀:
闲太久,自动走。
三、流程口诀速记
提任务,先核心,队列排,扩满员;全满员,策略管;空闲久,自动走。
四、常用方法与内部逻辑简表
阶段 | 关键方法/类 | 主要逻辑说明 |
---|---|---|
任务提交 | execute/submit | 任务进线程池 |
核心线程判断 | addWorker(core=true) | 核心线程是否有空位,有则新建 |
入队列 | workQueue.offer | 核心线程满,队列没满则排队 |
非核心线程 | addWorker(core=false) | 队列满,是否可新建非核心线程 |
拒绝策略 | RejectedExecutionHandler | 全部满员,执行拒绝策略 |
线程回收 | allowCoreThreadTimeOut | 非核心线程闲置超时自动销毁 |
五、源码脉络图(伪代码)
execute(command) {if (核心线程未满)addWorker(command, true) // 新核心线程else if (队列未满)workQueue.offer(command) // 入队列else if (线程池未满)addWorker(command, false) // 新非核心线程elsereject(command) // 拒绝策略
}
六、速记口诀总结
场景 | 口诀 |
---|---|
任务进池 | 先核心,后队列 |
队列满了 | 再扩容(到最大线程数) |
全满员 | 策略管(拒绝策略) |
线程回收 | 闲太久,自动走 |
全流程 | 提任务,先核心,队列排,扩满员;全满员,策略管;空闲久,自动走。 |
七、配图(流程图)
+-------------------------+| 提交任务 |+-------------------------+|+-------------+-------------+| |
+-----v-----+ +-----v-----+
| 核心线程? |----是-------->| 创建线程 |
+-----------+ +-----------+|否|
+-----v-----+
| 队列满? |----否-------> 入队列
+-----------+|是|
+-----v-----+
| 最大线程? |----否-------> 创建线程
+-----------+|是|
+-----v-----+
| 拒绝策略 |
+-----------+
八、结语
通过以上细化,线程池的工作原理、源码关键点、方法流程、口诀速记都一目了然。
只要记住口诀和流程图,结合源码细节,面试和实战都能轻松拿捏!