JVM 中的守护线程
生活随笔
收集整理的這篇文章主要介紹了
JVM 中的守护线程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在之前的《詳解JVM如何處理異常》提到了守護線程,當時沒有詳細解釋,所以打算放到今天來解釋說明一下JVM守護線程的內容。
特點
- 通常由JVM啟動
- 運行在后臺處理任務,比如垃圾回收等
- 用戶啟動線程執行結束或者JVM結束時,會等待所有的非守護線程執行結束,但是不會因為守護線程的存在而影響關閉。
判斷線程是否為守護線程
判斷一個線程是否為守護線程,主要依據如下的內容
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | /* Whether or not the thread is a daemon thread. */ private boolean daemon = false;/** * Tests if this thread is a daemon thread. * * @return <code>true</code> if this thread is a daemon thread; * <code>false</code> otherwise. * @see #setDaemon(boolean) */ public final boolean isDaemon() {return daemon; } |
下面我們進行一些簡單的代碼,驗證一些關于守護線程的特性和一些猜測。
輔助方法
打印線程信息的方法,輸出線程的組,是否為守護線程以及對應的優先級。
?
| 1 2 3 4 5 6 7 8 9 | private static void dumpAllThreadsInfo() {Set<Thread> threadSet = Thread.getAllStackTraces().keySet();for(Thread thread: threadSet) {System.out.println("dumpAllThreadsInfo thread.name=" + thread.getName()+ ";group=" + thread.getThreadGroup()+ ";isDaemon=" + thread.isDaemon()+ ";priority=" + thread.getPriority());} } |
線程睡眠的方法
?
| 1 2 3 4 5 6 7 8 | private static void makeThreadSleep(long durationInMillSeconds) {try {Thread.sleep(durationInMillSeconds);} catch (InterruptedException e) {e.printStackTrace();}} |
驗證普通的(非守護線程)線程會影響進程(JVM)退出
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private static void testNormalThread() {long startTime = System.currentTimeMillis();new Thread("NormalThread") {@Overridepublic void run() {super.run();//保持睡眠,確保在執行dumpAllThreadsInfo時,該線程不會因為退出導致dumpAllThreadsInfo無法打印信息。makeThreadSleep(10 * 1000);System.out.println("startNormalThread normalThread.time cost=" + (System.currentTimeMillis() - startTime));}}.start();//主線程暫定3秒,確保子線程都啟動完成makeThreadSleep(3 * 1000);dumpAllThreadsInfo();System.out.println("MainThread.time cost = " + (System.currentTimeMillis() - startTime)); } |
獲取輸出日志
?
| 1 2 3 4 5 6 7 8 9 10 | dumpAllThreadsInfo thread.name=Signal Dispatcher;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=9 dumpAllThreadsInfo thread.name=Attach Listener;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=9 dumpAllThreadsInfo thread.name=Monitor Ctrl-Break;group=java.lang.ThreadGroup[name=main,maxpri=10];isDaemon=true;priority=5 dumpAllThreadsInfo thread.name=Reference Handler;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=10 dumpAllThreadsInfo thread.name=main;group=java.lang.ThreadGroup[name=main,maxpri=10];isDaemon=false;priority=5 dumpAllThreadsInfo thread.name=NormalThread;group=java.lang.ThreadGroup[name=main,maxpri=10];isDaemon=false;priority=5 dumpAllThreadsInfo thread.name=Finalizer;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=8 MainThread.time cost = 3009 startNormalThread normalThread.time cost=10003 Process finished with exit code 0 結束進程 |
我們根據上面的日志,我們可以發現
- startNormalThread normalThread.time cost=10003代表著子線程執行結束,先于后面的進程結束執行。
- Process finished with exit code 0?代表 結束進程
以上日志可以驗證進程是在我們啟動的子線程結束之后才退出的。
驗證JVM不等待守護線程就會結束
其實上面的例子也可以驗證JVM不等待JVM啟動的守護線程(Reference Handler,Signal Dispatcher等)執行結束就退出。
這里我們再次用一段代碼驗證一下JVM不等待用戶啟動的守護線程結束就退出的事實。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private static void testDaemonThread() {long startTime = System.currentTimeMillis();Thread daemonThreadSetByUser = new Thread("daemonThreadSetByUser") {@Overridepublic void run() {makeThreadSleep(10 * 1000);super.run();System.out.println("daemonThreadSetByUser.time cost=" + (System.currentTimeMillis() - startTime));}};daemonThreadSetByUser.setDaemon(true);daemonThreadSetByUser.start();//主線程暫定3秒,確保子線程都啟動完成makeThreadSleep(3 * 1000);dumpAllThreadsInfo();System.out.println("MainThread.time cost = " + (System.currentTimeMillis() - startTime)); } |
上面的結果得到的輸出日志為
?
| 1 2 3 4 5 6 7 8 9 10 | dumpAllThreadsInfo thread.name=Signal Dispatcher;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=9 dumpAllThreadsInfo thread.name=Attach Listener;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=9 dumpAllThreadsInfo thread.name=Monitor Ctrl-Break;group=java.lang.ThreadGroup[name=main,maxpri=10];isDaemon=true;priority=5 dumpAllThreadsInfo thread.name=Reference Handler;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=10 dumpAllThreadsInfo thread.name=main;group=java.lang.ThreadGroup[name=main,maxpri=10];isDaemon=false;priority=5 dumpAllThreadsInfo thread.name=daemonThreadSetByUser;group=java.lang.ThreadGroup[name=main,maxpri=10];isDaemon=true;priority=5 dumpAllThreadsInfo thread.name=Finalizer;group=java.lang.ThreadGroup[name=system,maxpri=10];isDaemon=true;priority=8 MainThread.time cost = 3006Process finished with exit code 0 |
我們可以看到,上面的日志沒有類似daemonThreadSetByUser.time cost=的信息。可以確定JVM沒有等待守護線程結束就退出了。
注意:
- 新的線程是否初始為守護線程,取決于啟動該線程的線程是否為守護線程。
- 守護線程默認啟動的線程為守護線程,非守護線程啟動的線程默認為非守護線程。
- 主線程(非守護線程)啟用一個守護線程,需要調用Thread.setDaemon來設置啟動線程為守護線程。
關于Priority與守護線程的關系
有一種傳言為守護線程的優先級要低,然而事實是
- 優先級與是否為守護線程沒有必然的聯系
- 新的線程的優先級與創建該線程的線程優先級一致。
- 但是建議將守護線程的優先級降低一些。
感興趣的可以自己驗證一下(其實上面的代碼已經有驗證了)
from:?https://droidyue.com/blog/2018/12/16/daemon-thread-in-java/?
總結
以上是生活随笔為你收集整理的JVM 中的守护线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线程管理(七)守护线程的创建和运行
- 下一篇: java class类文件结构