守护线程与非守护线程

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

相关推荐
CV_J4 小时前
安装kibana
java·elasticsearch·spring cloud·docker·容器
码农水水6 小时前
国家电网Java面试被问:TCP的BBR拥塞控制算法原理
java·开发语言·网络·分布式·面试·wpf
qq_336313937 小时前
java基础-网络编程-TCP
java·网络·tcp/ip
咕噜咕噜啦啦7 小时前
Java期末习题速通
java·开发语言
盐真卿7 小时前
python2
java·前端·javascript
一嘴一个橘子8 小时前
mybatis - 动态语句、批量注册mapper、分页插件
java
组合缺一8 小时前
Json Dom 怎么玩转?
java·json·dom·snack4
危险、9 小时前
一套提升 Spring Boot 项目的高并发、高可用能力的 Cursor 专用提示词
java·spring boot·提示词
kaico20189 小时前
JDK11新特性
java
钊兵9 小时前
java实现GeoJSON地理信息对经纬度点的匹配
java·开发语言