bigywater.tech
Java · 390

ThreadFactory:线程身份与生命周期

线程池里每个线程的名字、daemon、异常处理和上下文,都是由 ThreadFactory 与线程第一次被创建时的环境决定的。这篇逐项量出:哪些属性继承、哪些不继承,异常去哪了,上下文是什么时候被固定的,以及线程什么时候才真正创建。

0.1 s vs 5 s+
main 结束后没有 shutdown 的线程池:daemon 线程池让 JVM 退出的用时 vs 非 daemon 线程池(5 秒后仍未退出)
custom-loader
由 submitter 线程第一次提交时创建的池线程的 context ClassLoader(main 线程自己是 AppClassLoader)
1, 2, 2, 2, 3, 4
ThreadPoolExecutor(2, 4, 队列 2) 连续提交 6 个任务后,ThreadFactory 的累计调用次数

线程身份与继承

实测 · 本机执行输出
  main 线程里 new Thread:Thread-0(daemon=false, prio=5)
  在 daemon 线程里 new Thread:Thread-2(daemon=true, prio=5);在 daemon 线程里用 defaultThreadFactory:pool-1-thread-1(daemon=false, prio=5)
  在优先级 9 的线程里 new Thread:Thread-4(daemon=false, prio=9)
  固定线程池 2 个线程的默认名字:[pool-2-thread-1(daemon=false, prio=5) 组 main, pool-2-thread-2(daemon=false, prio=5) 组 main]

未捕获异常

实测 · 本机执行输出
  submit:handler 没有被调用;Future.get() 抛 ExecutionException(submit 里的异常)
  第二个任务在 worker-1 上运行
  handler 收到:线程 worker-1,异常 execute 里的异常
  execute 抛异常之后的任务在 worker-2 上运行
  ThreadFactory 一共创建了 2 个线程

创建时拍下的上下文

实测 · 本机执行输出
  池线程(由 submitter 线程第一次提交时创建)的 context ClassLoader = custom-loader,InheritableThreadLocal = submitter-的值
  main 线程现在的 context ClassLoader = AppClassLoader(app),InheritableThreadLocal = main-的值;main 再提交任务也不会更新池线程的这两个值

daemon 与 JVM 退出

实测 · 本机执行输出
  exit-nondaemon:输出 "main 结束,没有 shutdown(非 daemon 线程池)";5 秒后 JVM 仍未退出(被杀掉)
  exit-daemon:输出 "main 结束,没有 shutdown(daemon 线程池)";JVM 在 0.1 s 内退出,退出码 0

惰性创建与虚拟线程

实测 · 本机执行输出
  new ThreadPoolExecutor(2, 4, 队列 2):构造后 ThreadFactory 调用 0 次(线程是惰性创建的)
  连续提交 6 个阻塞任务:第 1 个任务后 1 次,第 2 个任务后 2 次,第 3 个任务后 2 次,第 4 个任务后 2 次,第 5 个任务后 3 次,第 6 个任务后 4 次
  任务结束 500 ms 后(keepAlive 100 ms,默认不回收核心线程):池大小 2,ThreadFactory 累计 4 次
  allowCoreThreadTimeOut(true) 后再等 500 ms:池大小 0
  虚拟线程:ThreadFactory 生成的名字 vt-0、vt-1,isDaemon() = true,isVirtual() = true,setPriority(10) 之后 getPriority() = 5;setDaemon(false) 会抛 IllegalArgumentException:'false' not legal for virtual threads

分步演示

ThreadFactory
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;自写的 ThreadFactory 与最小任务
    • 没有验证:Spring ThreadPoolTaskExecutor / Tomcat 线程池的命名与上下文传播、TransmittableThreadLocal 等传播方案、结构化并发(StructuredTaskScope)的线程上下文、SecurityManager / 访问控制上下文的继承(JDK 21 已弃用)
    京ICP备2026067502号-1京公网安备11010502063487号