mainLock;
mainLock.lock();
try {
for (MyWorker w : workers) {
Thread t = w.thread;
// 1. t.isInterrupted(),说明当前线程存在中断信号,之前已经被中断了,无需再次中断
// 2. w.tryLock(), runWorker方法中如果工作线程获取到任务开始工作,会先进行Lock加锁
// 则这里的tryLock会加锁失败,返回false。 而返回true的话,就说明当前工作线程是一个idle线程,需要被中断
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
// tryLock成功时,会将内部state的值设置为1,通过unlock恢复到未加锁的状态
w.unlock();
}
}
if (onlyOne) {
// 参数onlyOne为true,至多只中断一个工作线程
// 即使上面的t.interrupt()没有执行,也在这里跳出循环
break;
}
}
} finally {
mainLock.unlock();
}
}
/**
* 单独为jdk的ScheduledThreadPoolExecutor开的一个钩子函数
* 由ScheduledThreadPoolExecutor继承ThreadExecutor时重写(包级别访问权限)
* */
void onShutdown() {}
- shutdown方法在入口处使用mainLock加锁后,通过checkShutdownAccess检查当前是否有权限访问工作线程(前提是设置了SecurityManager),如果无权限则会抛出SecurityException异常。
- 通过advanceRunState方法将线程池状态推进到SHUTDOWN。
- 通过interruptIdleWorkers使用中断指令(Thread.interrupt)唤醒所有处于idle状态的工作线程(存在idle状态的工作线程代表着当前工作队列是空的)。
idle的工作线程在被唤醒后从getTask方法中退出(getTask中对应的退出逻辑在下文中展开),进而退出runWorker方法,最终系统回收掉工作线程占用的各种资源(第一篇博客中runWorker的解析中提到过)。
- 调用包级别修饰的钩子函数onShutdown。这一方法是作者专门为同为java.util.concurrent包下的ScheduledThreadPoolExecutor提供的拓展,不在本篇博客中展开。
- 前面提到SHUTDOWN状态的线程池在工作线程都全部退出且工作队列为空时会转变为TIDYING状态,因此通过调用tryTerminate方法尝试终止线程池(当前不一定会满足条件,比如调用了shutdown但工作队列还有很多任务等待执行)。
tryTerminate方法中细节比较多,下文中再展开分析。
shutdownNow方法
shutdownNow方法同样用于关闭线程池,但比shutdown方法更加激进。shutdownNow方法令线程池从RUNNING状态转变为STOP状态,不再接收新任务,而工作队列中未完成的任务会以列表的形式返回给shutdownNow的调用者。
- shutdown方法在调用后,虽然不再接受新任务,但会等待工作队列中的队列被慢慢消费掉;而shutdownNow并不会等待,而是将当前工作队列中的所有未被捞取执行的剩余任务全部返回给shutdownNow的调用者,并对所有的工作线程(包括非idle的线程)发出中断通知。
- 这样做的好处是线程池可以更快的进入终止态,而不必等剩余的任务都完成,都返回给用户后也不会丢任务。
/**
* 立即关闭线程池(不再接收新任务,工作队列中未完成的任务会以列表的形式返回)
* @return 当前工作队列中未完成的任务
* */
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
// shutdown操作中涉及大量的资源访问和更新,直接通过互斥锁防并发
mainLock.lock();
try {
// 用于shutdown/shutdownNow时的安全访问权限
checkShutdownAccess();
// 将线程池状态从RUNNING推进到STOP
advanceRunState(STOP);
interruptWorkers();
// 将工作队列中未完成的任务提取出来(会清空线程池的workQueue)
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 尝试终止线程池
tryTerminate();
return tasks;
}
/**
* shutdownNow方法内,立即终止线程池时该方法被调用
* 中断通知所有已经启动的工作线程(比如等待在工作队列上的idle工作线程,或者run方法内部await、sleep等,令其抛出中断异常快速结束)
* */
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (MyWorker w : workers) {
// 遍历所有的worker线程,已启动的工作线程全部调用Thread.interrupt方法,发出中断信号
w.interruptIfStarted();
}
} finally {
mainLock.unlock();
}
}
/**
* 将工作队列中的任务全部转移出来
* 用于shutdownNow紧急关闭线程池时将未完成的任务返回给调用者,避免任务丢失
* */
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> queue = this.workQueue;
ArrayList<Runnable> taskList = new ArrayList<>();
queue.drainTo(taskList);
// 通常情况下,普通的阻塞队列的drainTo方法可以一次性的把所有元素都转移到taskList中
// 但jdk的DelayedQueue或者一些自定义的阻塞队列,drainTo方法无法转移所有的元素
// (比如DelayedQueue的drainTo方法只能转移已经不需要延迟的元素,即getDelay()<=0)
if (!queue.isEmpty()) {
// 所以在这里打一个补丁逻辑:如果drainTo方法执行后工作队列依然不为空,则通过更基础的remove方法把队列中剩余元素一个一个的循环放到taskList中
for (Runnable r : queue.toArray(new Runnable[0])) {
if (q