守护线程与非守护线程

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:"我还没完事,你得等着"。而守护线程则像是临时工,主程序结束它们就自动被辞退了。

相关推荐
wang09077 分钟前
自己动手写一个spring之IOC_2
java·后端·spring
来杯@Java19 分钟前
学生选课管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·maven·mybatis
不知名的老吴1 小时前
线程的生命周期之线程“插队“
java·开发语言·python
ANnianStriver1 小时前
PetLumina-02-后端开发与前后端联调
java·ai·sa-token
杨了个杨89822 小时前
Keepalived + Nginx + HAProxy 高可用架构部署实战案例
java·nginx·架构
马士兵教育4 小时前
Java还有前景吗?Java+AI大模型学习路线及项目?
java·人工智能·python·学习·机器学习
snow@li5 小时前
Java:理解 Gradle / 后端项目的管家 / 打包SpringBoot 应用 / 完成编译、下载依赖、运行测试、打包 JAR/WAR / 速查表
java
云烟成雨TD5 小时前
Spring AI 1.x 系列【57】动态工具发现:Tool Search Tool
java·人工智能·spring
zfoo-framework5 小时前
[修改代码使用]codex官方app中使用中转(不需要cc-switch) 1.config.toml 2.sk方式登录
java
逍遥德5 小时前
MQTT教程详解-05.SpringBoot集成mqtt client 性能分析
java·spring boot·spring·mt