守护线程与用户线程
本节内容主要是对守护线程与用户线程进行深入的讲解,具体内容点如下:
-
了解守护线程与用户线程的定义及区别,是我们学习本节内容的基础知识点。
-
了解守护线程的特点,是我们掌握守护线程的第一步。
-
掌握守护线程的创建,是本节内容的重点。
-
通过守护线程与JVM的退出实验,我们可以更加深入地理解守护线程的地位以及作用,为本节内容次重点。
-
了解守护线程的作用及使用场景,为后续开发过程中创建守护线程奠定基础。
守护线程与用户线程的区别
Java 中的线程分为两类,分别为 daemon 线程(守护线程)和 user 线程(用户线程)。
在 JVM 启动时会调用 main 函数,main 函数所在的线程就是一个用户线程,其实在 JVM 内部同时还启动了很多守护线程,如垃圾回收线程。
守护线程定义:所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程。如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。
因此,当所有的非守护线程结束时,程序也就被终止,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会被终止。
用户线程定义:某种意义上的主要用户线程,只要有用户线程未执行完毕,JVM 虚拟机就不会退出。
区别:在本质上,用户线程和守护线程并没有太大区别,唯一的区别就是当最后一个非守护线程结束时,JVM 会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响 JVM 的退出。对于用户线程,只要有一个用户线程还没结束,正常情况下 JVM 就不会退出。
守护线程
Java 中的守护线程和 Linux 中的守护进程是有些区别的,Linux 守护进程是系统级别的,当系统退出时,它才会终止。
而 Java 中的守护线程是 JVM 级别的,当 JVM 中无任何用户进程时,守护进程销毁,JVM 退出,程序终止。Java 守护进程的最主要的特点如下:
-
守护线程是运行在程序后台的线程。
-
守护线程创建的线程,依然是守护线程。
-
守护线程不会影响 JVM 的退出,当 JVM 只剩余守护线程时,JVM 进行退出。
-
守护线程在 JVM 退出时,自动销毁。
那么,如何创建一个守护线程呢?答案是把线程转换为守护线程,即通过调用 Thread 对象的 setDaemon(true) 方法予以实现。
需要注意的是,thread.setDaemon(true) 必须在 thread.start() 之前被设置,否则会抛出一个 llegalThreadStateException 异常。也就是说:不能把正在运行的常规线程设置为守护线程;在 Daemon 线程中产生的新线程也是 Daemon 的。
在程序开发过程中,守护线程不应该用于访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作正在被执行时发生中断。
下面编写一个示例,演示如何创建一个守护线程。代码如下:
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread threadOne= new Thread(new Runnable() {
@Override public void run() {
// 省略用于实现逻辑功能的代码
}
});
threadOne.setDaemon(true); // 设置 threadOne 为守护线程
threadOne.start();
}
}
【例21.3】用户线程等待守护线程(实例位置:资源包\TM\sl\21\3)
创建 1 个线程,线程名为 threadOne;在 run() 方法中,线程休眠1000毫秒后,求解 1+2+3+…+100 的值;将线程 threadOne 设置为守护线程;加入 join() 方法,强制让用户线程等待守护线程 threadOne;最终输出的结果。代码如下:
public class Demo {
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();
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("用户线程:main()方法执行完毕");
}
}
运行结果如下:
守护线程:求和的结果为5050
用户线程:main()方法执行完毕
如果没有使用 join() 方法,那么守护线程还没来得及被执行,就随着用户线程一起消亡了。使用了 join() 方法后,待守护线程的执行完成后,再执行用户线程。但是,这样的操作是不允许的,因为守护线程默认就是服务于用户线程的,它们是不需要被用户线程等待的。
那么,什么时候适合用守护线程呢?具体如下:
-
为其他线程提供服务支持。
-
根据开发需求,程序结束时,这个线程必须正常且立刻被关闭。
-
心跳监听,垃圾回收,临时数据清理等通用服务。
如果一个正在执行某个操作的线程必须要将此操作执行完毕后再被释放,否则就会出现不良后果,那么这个线程就不可以是守护线程,而是用户线程。 |