learn-tech/专栏/Java并发编程78讲-完/17如何正确关闭线程池?shutdown和shutdownNow的区别?.md
2024-10-16 00:20:59 +08:00

97 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

因收到Google相关通知网站将会择期关闭。相关通知内容
17 如何正确关闭线程池shutdown 和 shutdownNow 的区别?
在本课时我们主要学习如何正确关闭线程池?以及 shutdown() 与 shutdownNow() 方法的区别?首先,我们创建一个线程数固定为 10 的线程池,并且往线程池中提交 100 个任务,如代码所示。
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
service.execute(new Task());
}
那么如果现在我们想关闭该线程池该如何做呢本课时主要介绍 5 种在 ThreadPoolExecutor 中涉及关闭线程池的方法如下所示
void shutdown;
boolean isShutdown;
boolean isTerminated;
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
List shutdownNow;
下面我们就对这些方法逐一展开
shutdown()
第一种方法叫作 shutdown()它可以安全地关闭一个线程池调用 shutdown() 方法之后线程池并不是立刻就被关闭因为这时线程池中可能还有很多任务正在被执行或是任务队列中有大量正在等待被执行的任务调用 shutdown() 方法后线程池会在执行完正在执行的任务和队列中等待的任务后才彻底关闭但这并不代表 shutdown() 操作是没有任何效果的调用 shutdown() 方法后如果还有新的任务被提交线程池则会根据拒绝策略直接拒绝后续新提交的任务
isShutdown()
第二个方法叫作 isShutdown()它可以返回 true 或者 false 来判断线程池是否已经开始了关闭工作也就是是否执行了 shutdown 或者 shutdownNow 方法这里需要注意如果调用 isShutdown() 方法的返回的结果为 true 并不代表线程池此时已经彻底关闭了这仅仅代表线程池开始了关闭的流程也就是说此时可能线程池中依然有线程在执行任务队列里也可能有等待被执行的任务
isTerminated()
第三种方法叫作 isTerminated()这个方法可以检测线程池是否真正终结这不仅代表线程池已关闭同时代表线程池中的所有任务都已经都执行完毕了因为我们刚才说过调用 shutdown 方法之后线程池会继续执行里面未完成的任务不仅包括线程正在执行的任务还包括正在任务队列中等待的任务比如此时已经调用了 shutdown 方法但是有一个线程依然在执行任务那么此时调用 isShutdown 方法返回的是 true 而调用 isTerminated 方法返回的便是 false 因为线程池中还有任务正在在被执行线程池并没有真正终结直到所有任务都执行完毕了调用 isTerminated() 方法才会返回 true这表示线程池已关闭并且线程池内部是空的所有剩余的任务都执行完毕了
awaitTermination()
第四个方法叫作 awaitTermination()它本身并不是用来关闭线程池的而是主要用来判断线程池状态的比如我们给 awaitTermination 方法传入的参数是 10 那么它就会陷入 10 秒钟的等待直到发生以下三种情况之一
等待期间包括进入等待状态之前线程池已关闭并且所有已提交的任务包括正在执行的和队列中等待的都执行完毕相当于线程池已经终结方法便会返回 true
等待超时时间到后第一种线程池终结的情况始终未发生方法返回 false
等待期间线程被中断方法会抛出 InterruptedException 异常
也就是说调用 awaitTermination 方法后当前线程会尝试等待一段指定的时间如果在等待时间内线程池已关闭并且内部的任务都执行完毕了也就是说线程池真正终结那么方法就返回 true否则超时返回 fasle
我们则可以根据 awaitTermination() 返回的布尔值来判断下一步应该执行的操作
shutdownNow()
最后一个方法是 shutdownNow()也是 5 种方法里功能最强大的它与第一种 shutdown 方法不同之处在于名字中多了一个单词 Now也就是表示立刻关闭的意思在执行 shutdownNow 方法之后首先会给所有线程池中的线程发送 interrupt 中断信号尝试中断这些任务的执行然后会将任务队列中正在等待的所有任务转移到一个 List 中并返回我们可以根据返回的任务 List 来进行一些补救的操作例如记录在案并在后期重试shutdownNow() 的源码如下所示
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
你可以看到源码中有一行 interruptWorkers() 代码,这行代码会让每一个已经启动的线程都中断,这样线程就可以在执行任务期间检测到中断信号并进行相应的处理,提前结束任务。这里需要注意的是,由于 Java 中不推荐强行停止线程的机制的限制,即便我们调用了 shutdownNow 方法,如果被中断的线程对于中断信号不理不睬,那么依然有可能导致任务不会停止。可见我们在开发中落地最佳实践是很重要的,我们自己编写的线程应当具有响应中断信号的能力,正确停止线程的方法在第 2 讲有讲过,应当利用中断信号来协同工作。
在掌握了这 5 种关闭线程池相关的方法之后,我们就可以根据自己的业务需要,选择合适的方法来停止线程池,比如通常我们可以用 shutdown() 方法来关闭,这样可以让已提交的任务都执行完毕,但是如果情况紧急,那我们就可以用 shutdownNow 方法来加快线程池“终结”的速度。