- 线程池是怎么实现核心线程保活的?
- 非核心线程超时销毁时怎么保证不误杀正在执行任务的线程
execute的调用链路
ThreadPoolExecutor.execute(Runnable command)
→ addWorker(Runnable firstTask, boolean core)
→ Worker.start()
→ Worker.run()
→ ThreadPoolExecutor.runWorker(Worker w)
→ getTask()
当线程池excute的时候,最终会getTask:getTask作用
| 核心线程保活 | 调用workQueue.take(),阻塞等待新任务 |
|---|---|
| 非核心线程超时 | 调用workQueue.poll(keepAliveTime, unit),超时返回null触发线程回收 |
- 线程启动后:首次通过
getTask()获取初始任务(firstTask) - 任务执行完成后:循环调用
getTask()获取新任务 - 线程销毁前:当
getTask()返回null时,触发processWorkerExit()
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
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;
}
}
}
非核心线程使用poll(keepAliveTime,...)获取任务
两种结果:
超时前获取到任务:继续执行任务,重置存活时间
超时未获取任务:执行compareAndDecrementWorkerCount()减少工作线程数并终止线程
- 只有空闲线程才会进入
getTask()的poll/take逻辑 - 正在执行任务的线程处于运行状态,不会检查超时
线程池的Worker类:继承AQS将其作为不可重入的互斥锁使用:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
// 锁状态定义
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) { // 通过CAS获取锁
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false; // 实现不可重入性
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0); // 释放锁
return true;
}
.....
}
- ## 不可重入的互斥锁:防止任务执行期间被中断
线程池在关闭时需要中断空闲线程,但正在执行任务的线程不能被中断(否则可能导致任务数据不一致)。
AQS:state
- 锁被持有(
state=1):线程正在执行任务(runWorker()中运行用户代码) - 锁未被持有(
state=0):线程正在getTask()中等待新任务(即空闲状态)
线程池的5种状态
| 状态 | 值 | 含义 |
|---|---|---|
| RUNNING | 111 | 正常运行状态 |
| SHUTDOWN | 000 | 不再接收新任务,处理队列任务 |
| STOP | 001 | 中断所有任务,不处理队列 |
| TIDYING | 010 | 所有任务终止,即将执行terminated() |
| TERMINATED | 011 | 完全终止 |
关键行为控制:
-
RUNNING:
- 允许创建新线程
- 允许任务入队
-
SHUTDOWN:
- 拒绝新任务(
execute()直接返回false) - 继续处理队列任务
getTask()返回null触发线程退出
- 拒绝新任务(
-
STOP:
- 中断所有工作线程
- 清空任务队列
- 立即进入TIDYING状态
状态转换时的队列唤醒
public void shutdown() {
advanceRunState(SHUTDOWN);
interruptIdleWorkers(); // 中断所有在take()阻塞的线程
onShutdown();
}
interruptIdleWorkers()
→ Worker.tryLock()获取锁成功(说明线程正在getTask()阻塞)
→ thread.interrupt()
→ workQueue.take()抛出InterruptedException
→ getTask()返回null
→ runWorker()退出循环
try{
while (task != null || (task = getTask()) != null)
}finally{
processWorkerExit(w, completedAbruptly);
}
→ processWorkerExit()回收线程
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}