守护线程与非守护线程

Q:守护线程不是应该是一种守护的存在嘛 就是保证程序不停止 为什么非守护线程才是这样

A:

非常好的问题!你指出了一个常见的理解误区。让我详细解释一下这个看似矛盾的现象。

关键理解:守护线程"守护"的对象不同

1. 守护线程的本质

java 复制代码
public class DaemonThreadDemo {
    public static void main(String[] args) {
        Thread daemonThread = new Thread(() -> {
            while (true) {
                System.out.println("守护线程在运行...");
                try { Thread.sleep(1000); } catch (InterruptedException e) {}
            }
        });
        
        daemonThread.setDaemon(true);  // 设置为守护线程
        daemonThread.start();
        
        System.out.println("主线程结束,程序立即退出(不会等待守护线程)");
    }
}
// 输出:
// 守护线程在运行...
// 主线程结束,程序立即退出(不会等待守护线程)
// 程序直接结束,守护线程被强制终止

2. 守护线程的真正含义

守护线程的"守护"意思是:

  • 守护的是JVM进程,而不是"守护程序不停止"
  • 当所有非守护线程结束时,JVM就会退出
  • JVM退出时,会强制终止所有还在运行的守护线程
  • 守护线程是为其他线程(非守护线程)服务的

3. 真实世界的类比

复制代码
医院急诊科场景:

非守护线程 = 急诊医生(必须工作到下班时间)
守护线程 = 保洁阿姨(医生下班了她也下班)

情况1:所有医生都下班了(非守护线程结束)
→ 保洁阿姨直接下班回家(守护线程被终止)
→ 急诊科关闭(JVM退出)

情况2:还有医生在值班(非守护线程存在)
→ 保洁阿姨继续工作(守护线程运行)
→ 急诊科保持开放(JVM运行)

4. Spring Boot中的实际线程分类

java 复制代码
public class SpringBootThreadAnalysis {
    public static void analyze() {
        // Spring Boot启动后典型的线程:
        
        // 非守护线程(保持JVM运行):
        // 1. Tomcat/Netty/Jetty的工作线程
        // 2. ScheduledExecutorService的核心线程
        // 3. 各种连接池的监控线程
        // 4. 消息监听线程
        
        // 守护线程(随JVM退出而终止):
        // 1. GC相关的后台线程
        // 2. JMX监控线程(部分)
        // 3. 某些后台清理线程
    }
}

5. 一个更具体的例子:Web服务器

java 复制代码
public class WebServerExample {
    public static void main(String[] args) {
        // 模拟Web服务器
        Thread serverThread = new Thread(() -> {
            System.out.println("Web服务器启动,监听8080端口...");
            while (true) {
                // 模拟处理请求
                try {
                    Thread.sleep(1000);
                    System.out.println("处理HTTP请求...");
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        
        // 关键决定:
        // 如果设置为守护线程:
        // serverThread.setDaemon(true);
        // main方法结束后,Web服务器线程立即被终止
        
        // 实际应该设置为非守护线程:
        serverThread.setDaemon(false); // 默认就是false
        serverThread.start();
        
        System.out.println("main方法执行完毕");
        // 但Web服务器线程是非守护线程,所以JVM继续运行
    }
}

6. 守护线程的典型用途

java 复制代码
public class DaemonThreadUseCases {
    public static void main(String[] args) {
        // 适合使用守护线程的场景:
        
        // 1. 垃圾回收监控(不关键)
        Thread gcMonitor = new Thread(() -> {
            while (true) {
                System.gc();
                try { Thread.sleep(60000); } catch (InterruptedException e) {}
            }
        });
        gcMonitor.setDaemon(true);  // 如果主程序结束,GC监控也不需要了
        
        // 2. 临时文件清理
        Thread tempCleaner = new Thread(() -> {
            while (true) {
                cleanTempFiles();
                try { Thread.sleep(3600000); } catch (InterruptedException e) {}
            }
        });
        tempCleaner.setDaemon(true);  // 程序结束,清理也终止
        
        // 3. 日志刷新(最后丢失一些日志可以接受)
        Thread logFlusher = new Thread(() -> {
            while (true) {
                flushLogs();
                try { Thread.sleep(5000); } catch (InterruptedException e) {}
            }
        });
        logFlusher.setDaemon(true);
        
        // 这些守护线程启动后
        // main线程做主要工作
        System.out.println("主程序开始工作...");
        // 主程序工作完成后退出
        // 所有守护线程自动终止
    }
}

7. Spring Boot中的实际配置

yaml 复制代码
# application.yml
server:
  tomcat:
    # 工作线程默认都是非守护的
    threads:
      max: 200
      min-spare: 10
java 复制代码
// Spring Boot创建线程池时的默认行为
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(50);
    executor.setThreadNamePrefix("async-");
    // 默认都是非守护线程,保证任务完成
    executor.setDaemon(false);  
    return executor;
}

总结澄清

常见的理解误区纠正:

错误理解 正确理解
守护线程是"守护程序不停止" 守护线程是"被守护"的,JVM退出时会被强制终止
需要守护线程来保持程序运行 需要非守护线程来保持程序运行
守护线程很重要,不能被终止 守护线程是辅助性的,可以随时被终止

记忆技巧:

  • 非守护线程 = 主人(JVM要等主人完成任务)
  • 守护线程 = 仆人(主人走了,仆人直接下班)
  • JVM只看还有没有**主人(非守护线程)**在工作

所以Spring Boot能持续运行,正是因为它创建了非守护线程(如Web服务器线程),这些线程告诉JVM:"我还没完事,你得等着"。而守护线程则像是临时工,主程序结束它们就自动被辞退了。

相关推荐
梦未17 小时前
Spring控制反转与依赖注入
java·后端·spring
喜欢流萤吖~17 小时前
Lambda 表达式
java
ZouZou老师17 小时前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式
曼巴UE517 小时前
UE5 C++ 动态多播
java·开发语言
VX:Fegn089517 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
程序员鱼皮17 小时前
刚刚,IDEA 免费版发布!终于不用破解了
java·程序员·jetbrains
Hui Baby18 小时前
Nacos容灾俩种方案对比
java
曲莫终18 小时前
Java单元测试框架Junit5用法一览
java
成富19 小时前
Chat Agent UI,类似 ChatGPT 的聊天界面,Spring AI 应用的测试工具
java·人工智能·spring·ui·chatgpt