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