本节内容主要是对守护线程与用户线程进行深入的讲解,具体内容点如下:
Java 中的线程分为两类,分别为 daemon 线程(守护线程〉和 user 线程(用户线程)。
在 JVM 启动时会调用 main 函数, main 函数所在的线程就是一个用户线程,其实在 JVM 内部同时还启动了好多守护线程,比如垃圾回收线程。
守护线程定义:所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程。比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。
因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程定义:某种意义上的主要用户线程,只要有用户线程未执行完毕,JVM 虚拟机不会退出。
区别:在本质上,用户线程和守护线程并没有太大区别,唯一的区别就是当最后一个非守护线程结束时,JVM 会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响 JVM 的退出。
言外之意,只要有一个用户线程还没结束, 正常情况下 JVM 就不会退出。
Java 中的守护线程和 Linux 中的守护进程是有些区别的,Linux 守护进程是系统级别的,当系统退出时,才会终止。
而 Java 中的守护线程是 JVM 级别的,当 JVM 中无任何用户进程时,守护进程销毁,JVM 退出,程序终止。总结来说,Java 守护进程的最主要的特点有:
创建方式:将线程转换为守护线程可以通过调用 Thread 对象的 setDaemon (true) 方法来实现。
创建细节:
线程创建代码示例:
public class DemoTest {
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
//代码执行逻辑
}
});
threadOne.setDaemon(true); //设置threadOne为守护线程
threadOne. start();
}
}
为了更好的了解守护线程与 JVM 是否退出的关系,我们首先来设计一个守护线程正在运行,但用户线程执行完毕导致的 JVM 退出的场景。
场景设计:
期望结果:
Tips:main 函数就是一个用户线程,main 方法执行时,只有一个用户线程,如果 main 函数执行完毕,用户线程销毁,JVM 退出,此时不会考虑守护线程是否执行完毕,直接退出。
代码实现 - 不加入 join 方法:
public class DemoTest {
public static void main(String[] args){
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum = sum + i;
}
System.out.println("守护线程,最终求和的值为: " + sum);
}
});
threadOne.setDaemon(true); //设置threadOne为守护线程
threadOne. start();
System.out.println("main 函数线程执行完毕, JVM 退出。");
}
}
执行结果验证:
main 函数线程执行完毕, JVM 退出。
从结果上可以看到,JVM 退出了,守护线程还没来得及执行,也就随着 JVM 的退出而消亡了。
代码实现 - 加入 join 方法:
public class DemoTest {
public static void main(String[] args){
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum = sum + i;
}
System.out.println("守护线程,最终求和的值为: " + sum);
}
});
threadOne.setDaemon(true); //设置threadOne为守护线程
threadOne. start();
try {
threadOne.join(); // 加入join 方法
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main 函数线程执行完毕, JVM 退出。");
}
}
执行结果验证:
守护线程,最终求和的值为: 5050
main 函数线程执行完毕, JVM 退出。
从结果来看,守护线程不决定 JVM 的退出,除非强制使用 join 方法使用户线程等待守护线程的执行结果,但是实际的开发过程中,这样的操作是不允许的,因为守护线程,默认就是不需要被用户线程等待的,是服务于用户线程的。
作用:我们以 GC 垃圾回收线程举例,它就是一个经典的守护线程,当我们的程序中不再有任何运行的 Thread, 程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是 JVM 上仅剩的线程时,垃圾回收线程会自动离开。
它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
应用场景:
掌握用户线程和守护线程的区别点非常重要,在实际的工作开发中,对一些服务型,通用型的线程服务可以根据需要选择守护线程进行执行,这样可以减少 JVM 不可退出的现象,并且可以更好地协调不同种类的线程之间的协作,减少守护线程对高优先级的用户线程的资源争夺,使系统更加的稳定。
本节的重中之重是掌握守护线程的创建以及创建需要注意的事项,了解守护线程与用户线程的区别使我们掌握守护线程的前提。
0/1000