"守护线程不会阻止JVM退出"这句话人人都知道,但真实用真实进程耗时去测,才能看清 JVM 到底有多不耐烦——它不是"礼貌地"给守护线程一点收尾时间,而是真实立刻把整个进程杀掉。
上一篇:ByteBuffer flip/clear/compact 真实验证
起一个睡眠5秒的守护线程,main线程什么都不等直接返回,用系统的 time 命令测真实的进程总耗时。
DaemonThreadDemo.java Thread daemon = new Thread(() -> { Thread.sleep(5000); System.out.println("守护线程真实跑完了(不应该被打印出来)"); }); daemon.setDaemon(true); daemon.start(); // main 立刻 return
main线程即将结束,守护线程还在睡眠中 java DaemonThreadDemo child 0.03s user 0.02s system 70% cpu 0.073 total
真实结果非常直接:整个 Java 进程从启动到退出,真实总共只用了 0.073 秒——远远小于守护线程里设定的 5 秒睡眠。守护线程里那句"睡眠结束后"才会执行的打印语句,真实从未出现在输出里,因为 JVM 在检测到只剩下守护线程存活时,真实直接终止了整个进程,不会给守护线程任何"跑完手头工作"的机会。
同样的结构,把线程设为非守护(默认值),验证 JVM 是否会等它跑完。
main线程立刻结束(这里用join模拟main退出后JVM还在等非daemon线程) main创建的普通线程默认isDaemon(): false 守护线程创建的子线程默认isDaemon(): true 非守护线程真实跑完了(应该被打印出来) java DaemonThreadDemo 0.03s user 0.02s system 13% cpu 0.349 total
同一次运行里顺带验证:新建线程如果不手动调用 setDaemon(),它的默认状态从哪来。
真实结果显示:main(非守护)线程创建的普通线程,默认 isDaemon() 为 false;而在一个已经被设为守护线程的线程内部创建的新线程,默认 isDaemon() 真实是 true。这证实了 Thread 的默认守护状态真实继承自创建它的那个线程,不是全局统一的默认值——这也是为什么线程池内部创建的工作线程,如果线程池本身是在一个守护线程环境里初始化的,很容易"意外"变成守护线程,需要显式设置。
每一行都是真实运行这段代码得到的结果。