java中关闭ExecutorService

关闭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() 等待过程中可能被其他线程中断,此时应恢复中断状态并做清理。

  • 捕获 InterruptedException 后,一般要调用 Thread.currentThread().interrupt()
  • 如果主线程被中断,又没做任何处理,可能导致关机逻辑被静默跳过
  • 不要只写空的 catch (InterruptedException e) {}

常见误区提醒

  • 不调用 shutdown() / shutdownNow() 就直接让 ExecutorService 被 GC? —— 危险!线程池中的工作线程默认是 non-daemon,JVM 不会退出,程序可能一直挂着
  • 只调用 shutdown() 就不管了? —— 如果任务卡住(比如死循环、无限 sleep),线程池永远不终止
  • 反复调用 shutdown()? —— 安全,多次调用无副作用;但 shutdownNow() 多次调用也无额外效果

基本上就这些。关键不是“怎么关”,而是“什么时候关、关到什么程度”。根据任务性质决定策略,再配合适当的等待和兜底处理。