Java中守护线程(Daemon Thread)和用户线程有什么区别?
在 Java 中,线程被分为两类:用户线程(User Thread)和守护线程(Daemon Thread)。
它们之间最核心的区别在于:它们对 JVM(Java 虚拟机)的退出机制影响不同。
以下是详细的区别和解析:
1. 核心区别:对 JVM 退出的影响
- 用户线程:JVM 会等待所有的用户线程执行完毕后才退出。只要还有一个用户线程在运行,JVM 就不会终止。
- 守护线程:JVM 不会等待守护线程执行完毕。当所有用户线程都结束时,JVM 会自动退出,并且会强行终止所有正在运行的守护线程。
2. 角色与作用
- 用户线程:通常用于执行应用程序的核心业务逻辑。比如我们平时通过
new Thread()创建的线程,或者main方法所在的线程,默认都是用户线程。 - 守护线程:通常用于在后台为其他线程(用户线程)提供服务或支持。最经典的例子就是垃圾回收线程(GC Thread)。 当没有用户线程运行时,垃圾回收也没有意义了,所以 JVM 会直接退出。
3. 如何创建守护线程
在 Java 中,创建的线程默认是用户线程。如果要将其变为守护线程,必须在调用 start() 方法之前,调用 setDaemon(true) 方法。
java
Thread thread = new Thread(() -> {
while (true) {
System.out.println("守护线程正在运行...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 将线程设置为守护线程(必须在 start() 之前调用)
thread.setDaemon(true);
thread.start();
4. 代码演示对比
看看下面这段代码,体会守护线程和用户线程的区别:
java
public class DaemonThreadDemo {
public static void main(String[] args) throws InterruptedException {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("后台守护线程在执行...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 如果注释掉下面这行,daemonThread 就是用户线程,程序会一直死循环运行下去。
// 如果保留这行,daemonThread 是守护线程,当 main 线程结束后,程序很快就会自动停止。
daemonThread.setDaemon(true);
daemonThread.start();
System.out.println("Main 线程(用户线程)开始执行...");
Thread.sleep(2000); // 模拟主线程工作 2 秒
System.out.println("Main 线程(用户线程)执行结束!");
}
}
5. 使用守护线程的注意事项(重要)
- 设置时机:
thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出IllegalThreadStateException异常。 - 继承性:在守护线程中创建的新线程,默认也是守护线程;在用户线程中创建的线程,默认是用户线程。
- 不要在守护线程中执行 I/O 操作或写数据库:这是非常危险的。因为当所有的用户线程结束后,JVM 会立即关闭,守护线程会被瞬间强行中断。此时,守护线程中的
finally块可能根本不会执行,这会导致文件流没有关闭、数据库连接未释放或数据写到一半损坏的情况。
总结表格
| 比较维度 | 用户线程 (User Thread) | 守护线程 (Daemon Thread) |
|---|---|---|
| 存在目的 | 执行程序核心业务逻辑 | 在后台为用户线程提供服务/支持 |
| JVM 退出 | JVM 会等待所有用户线程结束才退出 | JVM 不等待,一旦无用户线程,直接强行退出 |
| 默认状态 | main 线程及新创建的线程默认都是此类型 |
需要显式调用 setDaemon(true) 设置 |
| 典型代表 | Main 线程 | 垃圾回收器 (GC) 线程 |
| 适用场景 | 业务处理、数据计算等 | 内存清理、心跳检测、后台日志清理等 |