bigywater.tech
Java · 389

ExecutorService 关闭语义

JDK 19 起 ExecutorService 实现了 AutoCloseable,try-with-resources 看上去让线程池关闭变得简单。这篇逐一量出 close() 到底做了什么:什么时候阻塞、被中断时怎么办、对 commonPool 和定时线程池有什么特殊行为,以及 shutdownNow() 的边界。

909 ms
close() 等待 4 个各 500 ms、2 个线程的任务全部完成的用时(shutdown() 是 0 ms)
0 ms
ForkJoinPool.commonPool().close() 的用时:不等待、不关闭
1147 ms
ScheduledThreadPoolExecutor.close() 在 350 ms 时调用,等延迟 1500 ms 的任务的用时

四种收尾方式

实测 · 本机执行输出
  shutdown()                    调用返回用时 0 ms;1.2 s 后:完成 4 个、被中断 0 个,isTerminated() = true
  shutdown() + awaitTermination 调用返回用时 900 ms;1.2 s 后:完成 4 个、被中断 0 个,isTerminated() = true
  shutdownNow()                 调用返回用时 6 ms;1.2 s 后:完成 0 个、被中断 2 个,返回的未开始任务 2 个,isTerminated() = true
  close()                       调用返回用时 909 ms;1.2 s 后:完成 4 个、被中断 0 个,isTerminated() = true

close() 遇到中断

实测 · 本机执行输出
  150 ms 时中断了正在 close() 的线程:close() 在 159 ms 后返回,调用线程中断标志 = true;随后完成 0 个、被中断 2 个、未开始(被丢弃)2 个

commonPool

实测 · 本机执行输出
  commonPool().close() 用时 0 ms,返回时任务已完成 = false,isShutdown() = false;再提交一个任务仍然能执行
  自建 ForkJoinPool.close() 用时 441 ms,返回时任务已完成 = true,isTerminated() = true
  CompletableFuture.runAsync(默认用 commonPool)之后 commonPool().close() 用时 0 ms,返回时任务已完成 = false(close 没有等它)

定时线程池

实测 · 本机执行输出
  默认策略:getExecuteExistingDelayedTasksAfterShutdownPolicy() = true,getContinueExistingPeriodicTasksAfterShutdownPolicy() = false
  350 ms 时 close():用时 1147 ms(延迟 1500 ms 的一次性任务被等到);周期任务在 close 前执行了 4 次、close 后共 4 次;延迟任务已执行 = true
  setContinueExistingPeriodicTasksAfterShutdownPolicy(true) + shutdown() 后 600 ms:周期任务共执行 8 次,isTerminated() = false(周期任务不停,池永远不会终止)

不响应中断的任务

实测 · 本机执行输出
  shutdownNow() 立即返回(0 ms),未开始任务 0 个;此时任务已结束 = false,isTerminated() = false
  awaitTermination(300 ms) = false(距 shutdownNow 约 302 ms);任务已结束 = false
  继续 awaitTermination(5 s) = true,距 shutdownNow 共约 1399 ms(任务忙等 1500 ms,从提交后 100 ms 才调用 shutdownNow);任务已结束 = true
  对照:JDK 17 与 JDK 21 上同样的 try-with-resources
  JDK 17 javac 退出码 1 | <jtmp>/j249b/Main.java:1: error: incompatible types: try-with-resources not applicable to variable type
  JDK 21 javac 退出码 0 运行: ok

分步演示

ExecutorService 关闭
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 与 JDK 17.0.20(只用于编译对照);所有任务是自写的 sleep / 忙等任务
    • 没有验证:ThreadPerTaskExecutor(虚拟线程)的 close() 行为、close() 与 StructuredTaskScope 的关系、JDK 19 / 20 的行为、ThreadPoolExecutor 子类重写 terminated() 的时机
    京ICP备2026067502号-1京公网安备11010502063487号