守护线程
知识点
- 什么是守护线程?
守护线程也是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)。