了解下JUC的线程池学习十(shutdown方法源码分析)
作者:互联网
1.介绍
shutdown()方法中会通过interruptIdleWorkers()中断所有的空闲线程,
这个时候有可能有非空闲的线程在执行某个任务,执行任务完毕之后,
如果它刚好是核心线程,就会在下一轮循环阻塞在任务队列的take()方法,
如果不 做额外的干预,它甚至会在线程池关闭之后永久阻塞在任务队列的take()方法中。
为了避免这 种情况,每个工作线程退出的时候都会尝试中断工作线程集合中的某一个空闲的线程,
确保所有空闲的线程都能够正常退出。interruptIdleWorkers()方法中会对每一个工作线程先进行tryLock()判断,
只有返回true才有可能进行线程中断。我们知道runWorker()方法中,
工作线程在每次从任务队列中获取到非null的任务之后,会先进行加锁Worker#lock()操作,
这样就能避免线程在执行任务的过程中被中断,保证被中断的一定是空闲的工作线程。
2.shutdown方法源码分析(线程池关闭操作有几个相关的变体方法)
第一种:shutdown()
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 权限校验,安全策略相关判断
checkShutdownAccess();
// 设置SHUTDOWN状态
advanceRunState(SHUTDOWN);
// 中断所有的空闲的工作线程
interruptIdleWorkers();
// 钩子方法
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 调用上面分析果敢的尝试terminate方法,使状态更变为TIDYING,执行钩子方法terminated()后,最终状态更新为TERMINATED
tryTerminate();
}
// 升提状态
private void advanceRunState(int targetState) {
// assert targetState == SHUTDOWN || targetState == STOP;
for (;;) {
int c = ctl.get();
// 线程池状态至少为targetState或者CAS设置状态为targetState则跳出循环
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
// 中断所有的空闲的工作线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
第二种:shutdownNow()
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 权限校验,安全策略相关判断
checkShutdownAccess();
// 设置STOP状态
advanceRunState(STOP);
// 中断所有的工作线程
interruptWorkers();
// 清空工作队列并且取出所有的未执行的任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 调用上面分析果敢的尝试terminate方法,使状态更变为TIDYING,
//执行钩子方法terminated()后,最终状态更新为TERMINATED
tryTerminate();
return tasks;
}
// 遍历所有的工作线程,如果state > 0(启动状态)则进行中断
private void interruptWorkers() {
// assert mainLock.isHeldByCurrentThread();
for (Worker w : workers)
w.interruptIfStarted();
}
説明:shutdownNow()
方法会把线程池状态先更变为STOP
,
中断所有的工作线程(AbstractQueuedSynchronizer
的state
值大于0的Worker
实例,
也就是包括正在执行任务的Worker
和空闲的Worker
),然后遍历任务队列,
取出(移除)所有任务存放在一个列表中返回。
第三种:awaitTermination()
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
// 转换timeout的单位为纳秒
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 循环等待直到线程池状态更变为TERMINATED,每轮循环等待nanos纳秒
while (runStateLessThan(ctl.get(), TERMINATED)) {
if (nanos <= 0L)
return false;
nanos = termination.awaitNanos(nanos);
}
return true;
} finally {
mainLock.unlock();
}
}
说明:awaitTermination()
虽然不是shutdown()
方法体系,
但是它的处理逻辑就是确保调用此方法的线程会阻塞到tryTerminate()
方法
成功把线程池状态更新为TERMINATED
后再返回,
可以使用在某些需要感知线程池终结时刻的场景。
有一点值得关注的是:shutdown()
方法只会中断空闲的工作线程,
如果工作线程正在执行任务对象Runnable#run()
,
这种情况下的工作线程不会中断,
而是等待下一轮执行getTask()
方法的时候通过线程池状态判断正常终结该工作线程。
标签:JUC,targetState,mainLock,中断,源码,线程,方法,空闲 来源: https://www.cnblogs.com/HuiShouGuoQu/p/13602944.html