守护线程与用户线程的定义及区别
Java 中的线程分为两类,分别为 daemon 线程(守护线程)和 user 线程(用户线程)。
在 JVM 启动时会调用 main 函数, main 函数所在的线程就是一个用户线程,其实在 JVM 内部同时还启动了好多守护线程,比如垃圾回收线程。
守护线程定义:当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程定义:某种意义上的主要用户线程,只要有用户线程未执行完毕,JVM 虚拟机不会退出。
main函数所在线程是一个用户线程,我们自己创建的线程也是一个用户线程。即使主线程结束了,其他用户线程还没有结束,Java程序也不会结束。
守护线程
Java 中的守护线程和 Linux 中的守护进程是有些区别的,Linux 守护进程是系统级别的,当系统退出时,才会终止。
而 Java 中的守护线程是 JVM 级别的,当 JVM 中无任何用户进程时,守护进程销毁,JVM 退出,程序终止。总结来说,Java 守护进程的最主要的特点有:
- 守护线程是运行在程序后台的线程;
- 守护线程创建的线程,依然是守护线程;
- 守护线程不会影响 JVM 的退出,当 JVM 只剩余守护线程时,JVM 进行退出;
- 守护线程在 JVM 退出时,自动销毁
- 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
创建方式:将线程转换为守护线程可以通过调用 Thread 对象的 setDaemon (true) 方法来实现。
守护线程与 JVM 的退出实验
方案一
- 创建 1 个线程,线程名为 threadOne;
- run 方法线程 sleep 1000 毫秒后,进行求和计算,求解 1 + 2 + 3 + … + 100 的值;
- 将线程 threadOne 设置为守护线程;
- 执行代码,最终打印的结果;
方案二
- 在方案一基础上加入 join 方法,强制让用户线程等待守护线程 threadOne;
- 执行代码,最终打印的结果。
方案一结果
方法二结果
【分析】方法一产生的结果是因为,主线程(用户线程)执行完毕,JVM直接将守护线程销毁了。
【分析】方法二产生结果,是因为主线程等待守护线程执行完毕后才结束。
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
Thread daemon = new Thread(()->{
try {
Thread.sleep(1000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 设置守护线程
daemon.setDaemon(true);
daemon.start();
// daemon.join();
System.out.println("主线程结束");
}
}
守护线程的作用及使用场景
作用:我们以 GC 垃圾回收线程举例,它就是一个经典的守护线程,当我们的程序中不再有任何运行的 Thread, 程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是 JVM 上仅剩的线程时,垃圾回收线程会自动离开。
应用场景:
- 为其它线程提供服务支持的情况,可选用守护线程;
- 根据开发需求,程序结束时,这个线程必须正常且立刻关闭,就可以作为守护线程来使用;
- 如果一个正在执行某个操作的线程必须要执行完毕后再释放,否则就会出现不良的后果的话,那么这个线程就不能是守护线程,而是用户线程;
- 正常开发过程中,一般心跳监听,垃圾回收,临时数据清理等通用服务会选择守护线程