首页 > 编程笔记 > Java笔记 阅读:29

Java守护线程详解(附带实例)

Java 中的线程分为两类,分别为 daemon 线程(守护线程)和 user 线程(用户线程)。

在 JVM 启动时会调用 main 函数,main 函数所在的线程就是一个用户线程,其实在 JVM 内部同时还启动了很多守护线程,如垃圾回收线程。

守护线程定义:所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程。如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。

因此,当所有的非守护线程结束时,程序也就被终止,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会被终止。

用户线程定义:某种意义上的主要用户线程,只要有用户线程未执行完毕,JVM虚拟机就不会退出。

在本质上,用户线程和守护线程并没有太大区别,唯一的区别就是当最后一个非守护线程结束时,JVM 会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出。对于用户线程,只要有一个用户线程还没结束,正常情况下 JVM 就不会退出。

Java守护线程的创建

Java 中的守护线程和 Linux 中的守护进程是有些区别的,Linux 守护进程是系统级别的,当系统退出时,它才会终止。

而 Java 中的守护线程是 JVM 级别的,当 JVM 中无任何用户进程时,守护进程销毁,JVM 退出,程序终止。

Java 守护进程的最主要的特点如下:
那么,如何创建一个守护线程呢?答案是把线程转换为守护线程,即通过调用 Thread 对象的 setDaemon(true) 方法予以实现。

需要注意的是,thread.setDaemon(true) 必须在 thread.start() 之前被设置,否则会抛出一个 llegalThreadStateException 异常。也就是说,不能把正在运行的常规线程设置为守护线程;在守护线程中产生的新线程也是守护的。

在程序开发过程中,守护线程不应该用于访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作正在被执行时发生中断。

下面编写一个示例,演示如何创建一个守护线程。代码如下:
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();
    }
}

Java守护线程实例

【实例】用户线程等待守护线程。创建 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(); // 加入 join()方法
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("用户线程:main()方法执行完毕");
    }
}
运行结果如下:

守护线程:求和的结果为5050
用户线程:main()方法执行完毕

如果没有使用 join() 方法,那么守护线程还没来得及被执行,就随着用户线程一起消亡了。使用了 join() 方法后,待守护线程的执行完成后,再执行用户线程。但是,这样的操作是不允许的,因为守护线程默认就是服务于用户线程的,它们是不需要被用户线程等待的。

总结

那么,什么时候适合用守护线程呢?具体如下:
注意,如果一个正在执行某个操作的线程必须要将此操作执行完毕后再被释放,否则就会出现不良后果,那么这个线程就不可以是守护线程,而是用户线程。

相关文章