java 守护线程

守护线程是什么?为什么要使用守护线程?如何使用?

 

一、是什么?

Java的线程分为两种:UserThread(用户线程)、DaemonThread(守护线程)。

只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作。

只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作,DaemonThread的作用是为其他线程提供便利服务。守护线程最典型的应用就是GC。当存在用户线程时,GC必须保持持续工作来维护内存空间。

UserThread和DaemonThread两者几乎没有区别,唯一的不同之处就在于虚拟机的离开。如果UserThread已经全部退出运行了,只剩下DaemonThread存在了,虚拟机也就退出了。没有了被守护者,Daemon也就没有工作可做了,没有继续运行程序的必要。

举个例子:如果当前程序中不再有任何运行中的UserThread,程序就不会再产生垃圾,GC就无事可做了。所以当GC的线程是Java虚拟机上仅剩的线程时,Java虚拟机就会自动退出。

二、为什么要使用守护线程?

其实我感觉并没有什么情景一定要去使用守护线程(非守护线程不可),而是某些情景比较适合使用守护线程(就像GC)。要搞清楚守护线程适合干什么,首先要明确守护线程的特点:

1.所有UserThread停止了,DaemonThread也要停止。

其实这个特点通过普通的Thread也可以进行模仿。如果是我的话,就会尝试这样去实现:

把所有启动的UserThread放在UserThreadList中,DaemonThread放在另一个DaemonThreadList中。

一旦某个UserThread准备停止了,检查一下List中是否还有其他活着的UserThread。如果还有,当前的UserThread结束,从UserThreadList中移除。如果没有其他活着的UserThread了,就通过interrupt方法来通知DaemonThreadList中的所有DaemonThread:你需要停止了,然后所有的DaemonThread就在各自的Thread中自己停止。

在实际开发时,只需要把某条线程在开启前使用setDaemon方法设为守护线程就可以了。守护线程有自己的中断机制,不需要我们手动管理。我们只需要找好适合的使用场景(这是最重要的),安排好守护线程的工作(像是GC的话,就是整理内存)即可,守护线程自己知道什么时候该停止,这就省去了我们手动管理线程的麻烦。

2.换言之,根本不知道DaemonThread什么时候会停止

因为不知道在所有的UserThread会在什么时候停止,所以我们也不知道DaemonThread什么时候会停止。如果使用DaemonThread做读写和计算的功能,很可能运行到一半就停止了,结果肯定会有问题。

所以个人认为DaemonThread不适合做读写和计算的功能。在考虑使用场景时,要选择没有严重后果的功能给DaemonThread去实现。

举个例子:所有UserThread都停止了,那么JVM就会退出,停止过程中将关闭所有的DaemonThread(包括GC)。就算GC没有清理完也无所谓,因为JVM退出了,JVM的内存空间将会重置,所以不会有严重后果。

综上,守护线程适合做一些没有严重后果的辅助工作(除了GC,我好像举不出来其他例子了…)。

三、如何使用守护线程?

1.设置守护线程

就算userThread运行结束了,userThread所创建的daemonThread依然会一直运行下去。

但是如果在开启daemonThread之前,使用setDaemon方法将其设置为守护线程:

再运行,就会发现daemonThread在userThread运行结束之后就直接停止了,连System.out.println(“userThread end”);都输出不了。

这也间接证明了守护线程往往是不太可靠的,不要进行读写计算工作。

2.这里还是要强调一下,所有的用户线程都停止,守护线程才会停止

如果把主线程改成:

就算userThread停止了,主线程依然在运行。所以daemonThread并不会停止。

3.启动用户线程之后,就无法再将其变为守护线程了

调换一下顺序:

那么运行时会抛出错误:

设置失败的用户线程依然会继续运行。

4.守护线程中启动的线程,依然是守护线程

我个人暂时还不能理解其中的原因…

在这里,我并没有刻意地把daemonThreadChild设为守护线程,只是在daemonThread中将其启动了,结果为:

可以看到,daemonThreadChild依然是守护线程。

四、总结

感觉平时写并发的时候用得并不多…

发表评论

电子邮件地址不会被公开。 必填项已用*标注