关闭ExecutorService的核心是先拒绝新任务、再尽量完成已有任务、最后释放资源;应根据任务重要性与阻塞性选择shutdown()(温和等待)或shutdownNow()(立即中断),并配合awaitTermination()与异常处理确保正确终止。

关闭 ExecutorService 的核心是**先拒绝新任务,再尽量完成已有任务,最后释放资源**。直接调用 shutdown() 或 shutdownNow() 是标准做法,但具体选哪个、后续是否等待,得看业务需求。
shutdown():温和关闭,等任务自然结束
调用 shutdown() 后,线程池不再接受新提交的任务(后续 submit() 或 execute() 会抛 RejectedExecutionException),但会继续执行已加入队列的和正在运行的任务。
- 适合任务重要、不能丢弃、且能预估执行时长的场景
- 通常配合
awaitTermination()使用,主动等待结束 - 示例:
executor.shutdown();
try {
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 超时后强制中断
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
shutdownNow():立即中断,尽力停止所有任务
尝试中断所有正在执行的任务,并返回尚未执行的任务列表(从任务队列中“拔出来”)。注意:中断只是建议,能否真正停止取决于任务自身是否响应中断(比如是否检查 Thread.interrupted() 或使用可中断的阻塞方法)。
- 适合任务可随时终止、或存在长时间阻塞风险(如网络等待)的场景
- 不会等待队列中任务执行,也不保证正在运行的任务一定停止
- 调用后仍建议检查返回的任务列表,按需处理
别忘了异常处理和线程中断传播
在 awaitTermination() 等待过程中可能被其他线程中断,此时应恢复中断状态并做清理。
立即学习“Java免费学习笔记(深入)”;
- 捕获
InterruptedException后,一般要调用Thread.currentThread().interrupt() - 如果主线程被中断,又没做任何处理,可能导致关机逻辑被静默跳过
- 不要只写空的
catch (InterruptedException e) {}
常见误区提醒
- 不调用 shutdown() / shutdownNow() 就直接让 ExecutorService 被 GC? —— 危险!线程池中的工作线程默认是 non-daemon,JVM 不会退出,程序可能一直挂着
- 只调用 shutdown() 就不管了? —— 如果任务卡住(比如死循环、无限 sleep),线程池永远不终止
-
反复调用 shutdown()? —— 安全,多次调用无副作用;但
shutdownNow()多次调用也无额外效果
基本上就这些。关键不是“怎么关”,而是“什么时候关、关到什么程度”。根据任务性质决定策略,再配合适当的等待和兜底处理。











