網上的資料中,守護線程的功能一般都是“只要當前JVM實例中尚存任何一個非守護線程沒有結束,守護線程就全部工作;只有當最后一個非守護線程結束是,守護線程隨着JVM一同結束工作,Daemon作用是為其他線程提供便利服務,守護線程最典型的應用就是GC(垃圾回收器),他就是一個很稱職的守護者。”
可是,我發現真實情況卻不是描述的這么回事,因為我對java也不懂,所以在此記錄一下守護線程中的一些問題。
我的思路是:
在main線程中建立兩個線程,一個線程A只打印一行信息就退出,它被設置成守護線程,另一個線程B通過sleep和循環來進行時間控制,讓它多運行一會,他被設置成非守護線程。如果按照上面的說明,我認為線程A怎么說也要和線程B的消亡時間是一樣的。實際情況卻不是這樣的。
下面是代碼:
文件 ThreadDaemon.java
package javaStudy.threadStudy; import java.util.Date; public class ThreadDaemon { public static void main(String[] args) { // Thread.currentThread().setDaemon(true); // Thread.currentThread().start(); // 系統的main函數使用一個主線程,主線程不是守護線程,也不能被設置成守護線程 System.out.println(Thread.currentThread().getName() + "\tprocess begin\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount\t" + Thread.currentThread().getThreadGroup().activeCount()); showThreadName(); // 新建一個線程,並將其設置成守護線程,他的操作僅僅是打印一行信息。 Thread t = new Thread(ThreadDaemon::daemonPrint); t.setDaemon(true); t.start(); System.out.println(Thread.currentThread().getName() + "\tafter create thread A\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount\t" + Thread.currentThread().getThreadGroup().activeCount()); showThreadName(); // 再建立一個線程,將其設置成用戶線程,即非守護線程。讓他多執行一會。 try { Thread.sleep(1000 * 3); Thread thread2 = new Thread(ThreadDaemon::print); thread2.setDaemon(false); thread2.start(); System.out.println(Thread.currentThread().getName() + "\tafter create thread B\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount\t" + Thread.currentThread().getThreadGroup().activeCount()); showThreadName(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\tExit:" + "\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount " + Thread.currentThread().getThreadGroup().activeCount()); showThreadName(); // System.exit(0) 是退出jvm。如果有此代碼,則子線程也會直接隨着主線程而退出。如果沒有此代碼,jvm會在子線程結束的時候而退出。 // System.exit(0); } // 用戶線程的調用 public static void print() { int counter = 1; while (counter < 5) { try { Thread.sleep(3 * 1000); // sleep for 10 seconds System.out.println(Thread.currentThread().getName() + "\tbefore Counter:" + counter++ + "\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount " + Thread.currentThread().getThreadGroup().activeCount()); Thread.sleep(3 * 1000); // sleep for 10 seconds System.out.println(Thread.currentThread().getName() + "\tafter Counter:" + counter++ + "\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount " + Thread.currentThread().getThreadGroup().activeCount()); showThreadName(); } catch (InterruptedException e) { e.printStackTrace(); } } } // 守護線程的調用 public static void daemonPrint() { try { Thread.sleep(6 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( Thread.currentThread().getName() + "\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount " + Thread.currentThread().getThreadGroup().activeCount()); showThreadName(); } // 顯示線程名稱 public static void showThreadName() { ThreadGroup currentGroup = Thread.currentThread().getThreadGroup(); int noThreads = currentGroup.activeCount(); Thread[] lstThreads = new Thread[noThreads]; currentGroup.enumerate(lstThreads); for (int i = 0; i < noThreads; i++) { System.out.println("==============="+lstThreads[i].getName() + "\t" + lstThreads[i].isDaemon()); } } }
以上代碼,我根據程序休眠的時間,讓主線程先退出,然后發現守護線程卻在第二個退出的。因為他不是死循環,所以提前退出了?這么說來,如果一個線程要想是守護線程的話,他必須是死循環?也就是說,從程序表面看,守護線程一定要比用戶線程存活的久一點?這么說來,將一個理論上活的更久的線程,設置成守護線程,僅僅是讓它自動和用戶線程一起消亡罷了。實際上,通過延長上面代碼的daemonPrint()的sleep的時間,也可以驗證守護線程會和用戶線程一起消亡。
下面是程序的日志:
main process begin Tue Oct 16 17:17:29 CST 2018 false thread amount 1
===============main false
main after create thread A Tue Oct 16 17:17:30 CST 2018 false thread amount 2
===============main false
===============Thread-0 true
main after create thread B Tue Oct 16 17:17:33 CST 2018 false thread amount 3
===============main false
===============Thread-0 true
===============Thread-1 false
main Exit: Tue Oct 16 17:17:33 CST 2018 false thread amount 3
===============main false
===============Thread-0 true
===============Thread-1 false
Thread-0 Tue Oct 16 17:17:36 CST 2018 true thread amount 3
===============Thread-0 true // 這一行日志,說明他是守護線程,但是下面卻沒有了他的名字。因為他不是死循環,所以提前退出了?
===============Thread-1 false
===============DestroyJavaVM false
Thread-1 before Counter:1 Tue Oct 16 17:17:36 CST 2018 false thread amount 2
Thread-1 after Counter:2 Tue Oct 16 17:17:39 CST 2018 false thread amount 2
===============Thread-1 false
===============DestroyJavaVM false
Thread-1 before Counter:3 Tue Oct 16 17:17:42 CST 2018 false thread amount 2
Thread-1 after Counter:4 Tue Oct 16 17:17:45 CST 2018 false thread amount 2
===============Thread-1 false
===============DestroyJavaVM false
結論:代碼的邏輯讓守護線程提前於用戶線程消亡的情況下,守護線程並不會主動延長生命和用戶線程一起消亡。但是,代碼的邏輯讓守護線程延遲於用戶線程消亡的情況下,守護線程會提前和用戶線程一起消亡。這樣也可以理解,畢竟CPU資源那么金貴,既然守護線程提前與用戶線程消亡,那他沒有必要賴着占用CPU的資源,對吧?