开发技能Java线程并发守护线程

守护线程

在 Java 中,线程分为两类:用户线程(User Thread) 和 守护线程(Daemon Thread)。守护线程是一种“后台”线程,它的存在依赖于用户线程:当所有用户线程都结束时,JVM 会自动退出,并强行终止所有仍在运行的守护线程,而不管它们是否执行完毕。

  • 为其他线程提供服务:守护线程通常用于执行一些后台任务,如垃圾回收(GC)、JVM 内部监控、心跳检测、日志记录等。
  • 不阻止 JVM 退出:只要还有用户线程在运行,JVM 就不会退出;一旦最后一个用户线程结束,JVM 就会停止,所有守护线程也会立即被终止(不会等待它们完成)。
  • 生命周期跟随用户线程:守护线程无需手动关闭,会随 JVM 一起消失。

使用 Thread.setDaemon(boolean on) 方法设置守护线程。

Thread daemonThread = new Thread(() -> {
    while (true) {
        System.out.println("守护线程运行中...");
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
    }
});
daemonThread.setDaemon(true);  // 必须在线程 start() 之前调用
daemonThread.start();
  • setDaemon(true) 必须在 start() 之前调用,否则会抛出 IllegalThreadStateException
  • 守护线程中创建的子线程默认也是守护线程(继承父线程的 daemon 状态)。

守护线程 vs 用户线程

特性用户线程守护线程
JVM 退出条件只要还有用户线程,JVM 就不会退出JVM 退出时无条件终止
典型用途业务逻辑处理后台辅助任务(GC、清理、监控)
是否能依赖可以依赖其正常执行完毕不能依赖其完整执行(可能被突然终止)
设置方式默认就是用户线程需要显式调用 setDaemon(true)

使用场景与注意事项

✅ 适合使用守护线程的场景

  • 垃圾回收(GC):JVM 内置的 GC 线程就是守护线程。
  • 自动缓存刷新、过期数据清理:即使被强行终止,下次重启还会重新开始,不会造成数据丢失。
  • 监控线程(如监控用户线程是否超时)
  • 心跳发送、日志异步输出:这些操作即使突然中断,通常不会影响核心业务。

❌ 不适合使用守护线程的场景

  • 执行关键业务逻辑:例如订单处理、银行转账,如果线程突然被终止,可能导致状态不一致。
  • 持有重要资源(文件、数据库连接、网络套接字):守护线程被强制结束时,资源可能无法正确释放(没有 finally 执行机会)。
  • 需要执行清理操作(如关闭文件、提交事务):因为 JVM 退出时不会等待守护线程的 finally 块或 shutdown hook

示例:守护线程中的 finally 不会执行


Thread daemon = new Thread(() -> {
    try {
        while (true) {
            System.out.println("工作");
        }
    } finally {
        System.out.println("finally 不会执行"); // 不会输出
    }
});
daemon.setDaemon(true);
daemon.start();
// 主线程结束后,JVM 退出,finally 中的代码永远不会运行

判断线程是否为守护线程

Thread t = new Thread();
System.out.println(t.isDaemon()); // false(默认用户线程)

总结

  • 守护线程:用于后台辅助任务,不阻止 JVM 退出,退出时会被强行终止。
  • 使用注意:不要在守护线程中执行需要确保完成的操作(如写文件、数据库事务),也不要在里面依赖 finally 做资源释放。
  • 常见守护线程:JVM 的 GC 线程、Finalizer 线程等。 通过合理使用守护线程,可以让程序在完成主要业务后自动退出,而不必手动关闭所有后台任务。但对于重要的、必须完成的操作,请使用用户线程。
Built with LogoFlowershow