一、线程池核心流程图
+-----------------+
| 提交任务 | submit/execute
+-----------------+
|
v
+-----------------+
| 判断核心线程数 | < corePoolSize?
+-----------------+
|Yes |No
v v
[创建新线程] +-----------------+
| 队列是否满? |
+-----------------+
|No |Yes
v v
[入队列排队] +------------------+
| 判断最大线程数 |
+------------------+
|No |Yes
v 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) // 新非核心线程
else
reject(command) // 拒绝策略
}
六、速记口诀总结
场景 | 口诀 |
---|---|
任务进池 | 先核心,后队列 |
队列满了 | 再扩容(到最大线程数) |
全满员 | 策略管(拒绝策略) |
线程回收 | 闲太久,自动走 |
全流程 | 提任务,先核心,队列排,扩满员;全满员,策略管;空闲久,自动走。 |
七、配图(流程图)
+-------------------------+
| 提交任务 |
+-------------------------+
|
+-------------+-------------+
| |
+-----v-----+ +-----v-----+
| 核心线程? |----是-------->| 创建线程 |
+-----------+ +-----------+
|
否
|
+-----v-----+
| 队列满? |----否-------> 入队列
+-----------+
|
是
|
+-----v-----+
| 最大线程? |----否-------> 创建线程
+-----------+
|
是
|
+-----v-----+
| 拒绝策略 |
+-----------+
八、结语
通过以上细化,线程池的工作原理、源码关键点、方法流程、口诀速记都一目了然。
只要记住口诀和流程图,结合源码细节,面试和实战都能轻松拿捏!