线程池的执行流程¶
一、execute() 源码流程¶
public void execute(Runnable command) {
int c = ctl.get();
// 1. 当前线程数 < core,直接创建核心线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 核心满了,入队列
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);
}
// 3. 队列满了,创建临时线程
else if (!addWorker(command, false))
reject(command); // 4. 到最大,拒绝
}
二、流程图¶
新任务
↓
线程数 < corePoolSize? → 创建核心线程执行
↓ 否
入队列 workQueue?
↓ 成功
队列里等
↓ 队列满
线程数 < maximumPoolSize? → 创建临时线程执行
↓ 否
拒绝策略 handler
三、addWorker¶
创建 Worker(包装一个线程),启动:
private boolean addWorker(Runnable firstTask, boolean core) {
// CAS 增加线程数
// 创建 Worker,启动线程
Worker w = new Worker(firstTask);
Thread t = w.thread;
t.start();
}
四、Worker 怎么循环取任务¶
Worker.run() → runWorker():
final void runWorker(Worker w) {
Runnable task = w.firstTask;
while (task != null || (task = getTask()) != null) {
// 执行任务
task.run();
task = null;
}
}
getTask() 从队列 take(),队列空就阻塞。
五、为什么先入队列再开线程¶
设计: 1. 核心线程够用,不开新线程。 2. 队列能扛住突发流量。 3. 队列满了才开临时线程,避免线程过多。
六、异常处理¶
任务里抛异常: - execute():异常会抛到调用方,线程终止。 - submit():异常被包装到 Future,get() 时才抛。
高频追问
- 为什么不直接开 maximum?因为队列能缓冲,避免线程过多。
- 核心线程能不能被回收?默认不能,allowCoreThreadTimeOut=true 可以。