守护线程

知识点

  • 什么是守护线程?

    守护线程也是java线程中的一种,和普通线程区别在于,它不会影响JVM的退出。即当所有非守护线程执行结束后,jvm退出,守护线程是否仍在执行不会影响JVM的判断。

  • 守护线程的作用是什么?

    守护线程作为守护者来监控用户线程的状况,比如dubbo monitor,gc。

  • 如何创建一个守护线程  

    在用户线程执行start()之前执行setDeamon(true)方法,该线程就变成了一个守护线程。

    在守护线程中创建的子线程,默认都是守护线程。

  • 守护线程何时结束

    所有的用户线程结束运行后,jvm退出,守护线程自动结束。

  • 守护线程注意事项

    正因为守护线程不知道什么时候就会被关闭,因此不建议参与具体业务执行,防止执行一半被强行结束。

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

守护线程

Java中的线程分为两种:

  • 用户线程
  • 守护线程

守护线程,顾名思义,就是用来守护用户线程的线程,如果进程中已经没有用户线程,那么守护线程也会被全部杀死。

可以理解为守护线程是一个保镖,用来保护和监督用户线程,如果被保护/监督的对象已经没有了,那么它也没有存在的必要。因此会结束守护,即被全部杀死。

 

如何创建守护线程

最常见的是将用户线程转换为守护线程

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread");
            }
        });
        // 将用户线程转换为守护线程
        thread.setDaemon(true);

 

同时还有一些其他的工具包也提供了守护线程,如Timer类等。

 

DEMO

先定义一个Thread,用来作为守护线程

class MyDaemon implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("当前时间:" + new Date());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

再定义一个我们的用户线程类

class MyThread implements Runnable {

    @Override
    public void run() {for (int i = 0; i < 5; i++) {
            System.out.println("用户线程:" + new Date());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

然后是我们的main函数

这里我们分两个情况来运行:

1.在main方法中运行守护线程

    public static void main(String[] args) throws InterruptedException {
        System.out.println("main线程:" + new Date());

        Thread thread = new Thread(new MyDaemon());
        // 将用户线程转换为守护线程
        thread.setDaemon(true);
        thread.start();

    }

运行结果:

main线程:Fri Mar 06 14:18:31 CST 2020
守护线程:Fri Mar 06 14:18:31 CST 2020

可以看到守护线程并没有一直执行下去,而是在main方法运行结束后被杀死。

假如我们给main方法增加5秒钟的休眠时间

    public static void main(String[] args) throws InterruptedException {
        System.out.println("main线程:" + new Date());

        Thread thread = new Thread(new MyDaemon());
        // 将用户线程转换为守护线程
        thread.setDaemon(true);
        thread.start();

        Thread.sleep(5000);
        System.out.println("main线程:" + new Date());
    }

运行结果:

main线程:Fri Mar 06 14:20:04 CST 2020
守护线程:Fri Mar 06 14:20:05 CST 2020
守护线程:Fri Mar 06 14:20:06 CST 2020
守护线程:Fri Mar 06 14:20:07 CST 2020
守护线程:Fri Mar 06 14:20:08 CST 2020
守护线程:Fri Mar 06 14:20:09 CST 2020
main线程:Fri Mar 06 14:20:10 CST 2020
守护线程:Fri Mar 06 14:20:10 CST 2020

同样的在main方法执行时间内(包括休眠),守护线程一直在正常执行,不断打印时间,main方法结束休眠,运行结束后,守护线程被杀死。

 

2.这一次我们在main方法中添加一个用户线程

    public static void main(String[] args) throws InterruptedException {
        System.out.println("main线程:" + new Date());

        Thread thread = new Thread(new MyDaemon());
        // 将用户线程转换为守护线程
        thread.setDaemon(true);
        thread.start();

        Thread thread2 = new Thread(new MyThread());
        thread2.start();
        System.out.println("main线程:" + new Date());
    }

运行结果:

main线程:Fri Mar 06 14:26:00 CST 2020
守护线程:Fri Mar 06 14:26:00 CST 2020
main线程:Fri Mar 06 14:26:00 CST 2020
用户线程:Fri Mar 06 14:26:00 CST 2020
守护线程:Fri Mar 06 14:26:01 CST 2020
用户线程:Fri Mar 06 14:26:01 CST 2020
守护线程:Fri Mar 06 14:26:02 CST 2020
用户线程:Fri Mar 06 14:26:02 CST 2020
守护线程:Fri Mar 06 14:26:03 CST 2020
用户线程:Fri Mar 06 14:26:03 CST 2020
守护线程:Fri Mar 06 14:26:04 CST 2020
用户线程:Fri Mar 06 14:26:04 CST 2020
守护线程:Fri Mar 06 14:26:05 CST 2020

可以看到,虽然main线程已经执行结束,但是还有一个用户线程仍在执行,因此守护线程并没有随着main线程的结束而被立即杀死,而是一直直到用户线程也执行完成,此时才最终被结束。

 

3.这一次,我们改为在守护线程中启动一个用户线程

 

守护线程中创建用户线程

class MyDaemon implements Runnable {

    @Override
    public void run() {
        Thread thread = new Thread(new MyThread());
        thread.start();
        System.out.println("当前线程是否守护线程:" + thread.isDaemon());
        while (true) {
            System.out.println("守护线程:" + new Date());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

main方法只启动守护线程

public static void main(String[] args) throws InterruptedException {
System.out.println("main线程:" + new Date());

Thread thread = new Thread(new MyDaemon());
// 将用户线程转换为守护线程
thread.setDaemon(true);
thread.start();
Thread.sleep(3000);
}

 运行结果:

main线程:Fri Mar 06 14:35:25 CST 2020
当前线程是否守护线程:true
守护线程:Fri Mar 06 14:35:25 CST 2020
用户线程:Fri Mar 06 14:35:25 CST 2020
守护线程:Fri Mar 06 14:35:26 CST 2020
用户线程:Fri Mar 06 14:35:26 CST 2020
守护线程:Fri Mar 06 14:35:27 CST 2020
用户线程:Fri Mar 06 14:35:27 CST 2020

从结果中可以看到,我们在守护线程中创建的虽然是一个用户线程,也并没有显示的将它设置为一个守护线程,但是在我们的打印结果中,它却表示它是一个守护线程!

既然它也是守护线程,我们这两个守护线程都在main方法执行的三秒多时间里不断地打印时间,随后随着main方法的结束,而一起结束。

 

总结

由上面的几个例子可知,守护线程是用来作为用户线程的守护者,它随着用户线程的结束而结束(即使守护线程代码是while(true)也会被强制结束)。

在守护线程中创建的线程,默认都是守护线程(除非用过setDaemon()方法指定它为非守护线程)。

 

应用

守护线程类似于用户线程的一个辅助,因此并不能用来运行一些和业务有关的代码。主要是用来监督当前线程的状态,比如作为监视器来使用(dubbo-monitor)。

 

posted @ 2020-03-06 14:54  一响贪欢  阅读(875)  评论(0编辑  收藏  举报