什么是守护线程?和普通线程有什么区别?
结论先行:守护线程(daemon thread)是不阻止 JVM 退出的线程。JVM 的退出条件只有一个:所有非守护线程都结束了。满足条件时,守护线程会被直接终止——它不保证执行完 finally,也不保证把手上的活干完。
1. 两者的差别
| 维度 | 用户线程(user thread) | 守护线程(daemon thread) |
|---|---|---|
| 是否阻止 JVM 退出 | 是,只要还有一个活着就不退 | 否 |
| 默认身份 | 是 | 否,由创建者继承 |
finally 是否保证执行 | 保证(正常结束或异常) | 不保证,进程退出即被掐断 |
| 典型例子 | main、业务线程池、Tomcat 的请求线程 | GC 线程、JIT 编译线程、JMX、日志清理 |
| 适合做的事 | 业务逻辑、订单、支付 | 后台辅助:心跳、清理、监控上报 |
2. JVM 的退出判定
JVM 退出条件:存活的非守护线程数 == 0
main 结束 ──▶ 还有用户线程活着吗?
├── 有 ──▶ 继续运行,JVM 不退出
└── 没有 ──▶ 强制终止所有守护线程(finally 不保证执行)──▶ JVM 退出
所以"守护线程"这个词的重点不在"守护",而在"附属":它的生命周期依附于用户线程,用户线程走光了,它也就没有存在的意义。
3. 代码验证
public class DaemonDemo {
public static void main(String[] args) {
Thread worker = new Thread(() -> {
try {
while (true) {
Thread.sleep(100);
System.out.println("守护线程还在干活");
}
} finally {
// 大概率不会打印:JVM 退出时不会等它
System.out.println("finally 执行了吗?");
}
}, "daemon-worker");
worker.setDaemon(true); // 必须在 start() 之前设置
worker.start();
System.out.println("main 结束,JVM 准备退出");
}
}
运行结果是"main 结束"后进程直接消失,那个 while(true) 并没有拖住 JVM。把 setDaemon(true) 去掉,程序就永远不结束了——这就是两者唯一但决定性的区别。
两个细节:
setDaemon()必须在线程启动前调用,启动后再调会抛IllegalThreadStateException;- 新线程继承创建者的守护标志:在守护线程里
new Thread()出来的线程默认也是守护线程。这一点最容易被忽略——后台任务里再开线程做业务,可能被"一起带走"。
4. 什么时候该用守护线程
| 场景 | 是否适合守护线程 | 原因 |
|---|---|---|
| 客户端心跳、定时上报 | 适合 | 主程序退出后无需补发 |
| 本地缓存清理 | 适合 | 数据可从源头重建 |
| 写数据库、发消息 | 不适合 | 进程退出会丢数据,且没有补偿机会 |
| 线程池工作线程 | 视情况 | 自定义 ThreadFactory 里常设 setDaemon(true),避免忘记 shutdown 导致进程不退出 |
Spring 的 @Async 池 | 一般不适合 | 业务任务要用用户线程,让容器能优雅停机 |
注意最后一行的实践价值:给线程池的线程设成守护线程,可以让"忘记关闭线程池"不至于阻塞 JVM 退出;但代价是停机时正在执行的任务会被硬砍。生产上更推荐显式调用 shutdown() + awaitTermination(),守护标识只作兜底。
5. 常见追问
追问一:main 线程是守护线程吗?
不是,main 是用户线程。JVM 启动时创建的第一个用户线程就是 main。
追问二:守护线程被终止时,锁会怎样? 线程被强制结束时,它持有的 monitor 会随线程销毁而释放,但临界区里的数据可能处于中间状态。所以绝对不要在守护线程里改共享的关键状态。
追问三:Runtime.addShutdownHook 和守护线程有什么区别?
关闭钩子(shutdown hook)是在 JVM 正常退出流程中执行的用户线程,能保证跑完(除非被 Runtime.halt 或 kill -9 打断);而守护线程是被动终止、毫无保证。需要"退出前收尾"就用钩子,不需要就用守护线程。
追问四:System.exit(0) 会等守护线程吗?
不会。System.exit 触发关闭流程,只等关闭钩子,然后直接终止所有线程。
小结
判断一个线程该不该是守护线程,只问一句:主程序退出时,这个任务丢掉可以接受吗?可以就给守护(后台辅助),不可以就必须用用户线程并做好优雅停机。