1. 线程池的状态
RUNNING
(1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
SHUTDOWN
(1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
STOP
(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
TIDYING
(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
TERMINATED
(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
2.关闭线程池的方法
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); //全局锁
try {
//查看调用shutdown的线程是否有权限结束线程池中的worker线程
checkShutdownAccess();
//变更标志位
advanceRunState(SHUTDOWN);
//此方法会调用interruptIdleWorkers方法,尝试中断所有线程。
//但任务队列不空,worker不会被关闭。
interruptIdleWorkers();
onShutdown(); //hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate(); //尝试结束线程池
}
其中做事情的是tryTerminate();
,其方法具体如下,可以看到时在尝试使用CAS方法来更改线程池状态
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//三种情况下,不去执行。
//1 状态为running,既运行时
//2 状态为tidying和terminated线程池中已经没有worker,不需要执行
//3 状态为shutdown,但队列中还有任务未执行,不能执行
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//尝试中断一个空闲线程,并返回。
//当shutdown方法被调用,worker消耗完任务后,会由于阻塞抛出异常后返回,结束runworker方法,最后调用此tryTerminate方法,关闭一个空闲线程。
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
//线程池中worker已经全部关闭
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //cas变更状态为tidying
try {
terminated(); //空方法
} finally {
ctl.set(ctlOf(TERMINATED, 0)); //变为terminated
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
总结:ThreadPoolExecutor如何防止shutdown方法在任务队列尚未完成时关闭线程的?
在以前的文章中也有描述,这里再次稍微总结一下。
1 worker在执行任务时(runWorker方法中)使用了自身的AQS不可重入锁,保证不会被interruptIdleWorkers执行中断。
2 worker在获取任务时(getTask方法中),并没有加锁,若worker被阻塞队列中阻塞,抛出的异常会被trycatch捕获,之后会从新进入新的循环判断是否需要退出。正常获取任务后,回到runworker方法中,会使用interrupted()方法,清空中断的标志位,并据继续执行获取的任务,防止执行任务时阻塞而抛出异常。
3 即使由于某些特殊原因,造成worker线程被中断,runWorker方法在最后会调用processWorkerExit方法重新创建worker线程。
其中processWorkerExit方法如下
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//completedAbruptly为true,属于worker非正常退出,线程池数量减一
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//添加进总完成线程数
completedTaskCount += w.completedTasks;
//移除worker
workers.remove(w);
} finally {
mainLock.unlock();
}
//判断是否需要结束线程池
tryTerminate();
int c = ctl.get();
//下边这段代码是用来判断是否需要添加worker的。
//首先线程池状态必须为running或shutdown,且满足以下两点任意一点
//1 该worker是非正常退出
//2 虽然该worker是正常退出,但是当前线程数小于等于规定线程数min(规定线程数,根据代码中的情况进行判断)
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false); //addworker方法仅仅是尝试增加
}
}