java線程是一個運用很廣泛的重點知識,我們很有必要了解java的daemon線程.
1.首先我們必須清楚的認識到java的線程分為兩類: 用戶線程和daemon線程
A. 用戶線程: 用戶線程可以簡單的理解為用戶定義的線程,當然包括main線程(以前我錯誤的認為main線程也是一個daemon線程,但是慢慢的發現原來main線程不是,因為如果我再main線程中創建一個用戶線程,並且打出日志,我們會發現這樣一個問題,main線程運行結束了,但是我們的線程任然在運行).
B. daemon線程: daemon線程是為我們創建的用戶線程提供服務的線程,比如說jvm的GC等等,這樣的線程有一個非常明顯的特征: 當用戶線程運行結束的時候,daemon線程將會自動退出.(由此我們可以推出下面關於daemon線程的幾條基本特點)
2. daemon 線程的特點:
A. 守護線程創建的過程中需要先調用setDaemon方法進行設置,然后再啟動線程.否則會報出IllegalThreadStateException異常.(個人在想一個問題,為什么不能動態更改線程為daemon線程?有時間一個補上這個內容,現在給出一個猜測: 是因為jvm判斷線程狀態的時候,如果當前只存在一個線程Thread1,如果我們把這個線程動態更改為daemon線程,jvm會認為當前已經不存在用戶線程而退出,稍后將會給出正確結論,抱歉!如果有哪位大牛看到,希望給出指點,謝謝!)
B. 由於daemon線程的終止條件是當前是否存在用戶線程,所以我們不能指派daemon線程來進行一些業務操作,而只能服務用戶線程.
C. daemon線程創建的子線程任然是daemon線程.
3. 針對上面給出的daemon線程的特點,我們進行如下驗證:
A. 對應上面的A特點:
public class ThreadTest { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Thread2()); thread1.start(); thread1.setDaemon(true); Thread.sleep(10); System.out.println("用戶線程退出"); } } class Thread2 implements Runnable{ @Override public void run() { try { Thread.sleep(1000); System.out.println("1+1="+(1+1)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 運行結果: Exception in thread "main" java.lang.IllegalThreadStateException at java.lang.Thread.setDaemon(Thread.java:1352) at ThreadTest.main(ThreadTest.java:5) 1+1=2
通過上面的例子我們可以發現,我們並不能動態的更改線程為daemon線程,源碼解釋如下:
java源碼: public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; }
我們可以發現,在源碼中如果我們的線程狀態是alive的,我們的程序就會拋出異常.
B.對應上面的B特點:
public class ThreadTest { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Thread2()); thread1.setDaemon(true); thread1.start(); Thread.sleep(10); System.out.println("用戶線程退出"); } } class Thread2 implements Runnable{ @Override public void run() { try { Thread.sleep(1000); System.out.println("1+1="+(1+1)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 運行結果: 用戶線程退出
通過上面的例子我們可以看到,我們在daemon線程中進行相關的計算工作,但是我們並沒有獲取計算結果,因為用戶線程main已經運行結束.
C.對應上面的C特點:
public class ThreadTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Thread2()); thread.setDaemon(true); thread.start(); System.out.println("Thread是否時daemon線程"+thread.isDaemon()); Thread.sleep(100); System.out.println("用戶線程退出"); } } class Thread2 implements Runnable{ @Override public void run() { Thread1 thread1 = new Thread1(); thread1.start(); System.out.println("Thread1是否是daemon線程"+thread1.isDaemon()); } } class Thread1 extends Thread{ public void run () { System.out.println("dosomething"); } } 運行結果: Thread是否時daemon線程true Thread1是否是daemon線程true dosomething 用戶線程退出
源碼解析:
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { ...... this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); ...... }
在進行Thread初始化的時候,會獲取父進程的isDaemon來復制子進程的daemon.
