1. 程式人生 > 其它 >Java 使用者執行緒和守護執行緒

Java 使用者執行緒和守護執行緒

Java中的執行緒

Java中存在兩種執行緒

  • 使用者執行緒
  • 守護執行緒

使用者執行緒:常用的普通執行緒均是使用者執行緒

守護執行緒:指在程式執行的時候在後臺提供一種通用服務的執行緒,守護執行緒是為使用者執行緒服務的。也就是說守護執行緒不依賴於終端,但是依賴於系統,與系統“同生共死”。

  • 通過Thread.setDaemon(false)設定為使用者執行緒;
  • 通過Thread.setDaemon(true)設定為守護執行緒。
  • 如果不設定次屬性,預設為使用者執行緒。
  • 這個函式務必線上程啟動前進行呼叫,否則會報java.lang.IllegalThreadStateException異常,啟動的執行緒無法變成守護執行緒,而是使用者執行緒。

 

使用者執行緒和守護執行緒的區別

1. 主執行緒結束後用戶執行緒還會繼續執行,JVM存活;主執行緒結束後守護執行緒和JVM的狀態又下面第2條確定。

2.如果JVM中所有的執行緒都是守護執行緒,那麼JVM就會退出,進而守護執行緒也會退出。如果JVM中還存在使用者執行緒,那麼JVM就會一直存活,不會退出。

 

由此可以得到:

守護執行緒是依賴於使用者執行緒,使用者執行緒退出了,守護執行緒也就會退出,典型的守護執行緒如垃圾回收執行緒

使用者執行緒是獨立存在的,不會因為其他使用者執行緒退出而退出。

 


 

  守護執行緒是一種特殊的執行緒,在後臺默默地完成一些系統性的服務,比如垃圾回收執行緒

JIT執行緒都是守護執行緒。與之對應的是使用者執行緒,使用者執行緒可以理解為是系統的工作執行緒,它會完成這個程式需要完成的業務操作。如果使用者執行緒全部結束了,意味著程式需要完成的業務操作已經結束了,系統可以退出了。所以當系統只剩下守護程序的時候,java虛擬機器會自動退出

 

測試程式碼

場景一:程式只有守護執行緒時,系統會自動退出,反之不會

 1 public class Demo1 {
 2 
 3     public static class T1 extends Thread {
 4         public T1(String name) {
 5             super
(name); 6 } 7 8 @Override 9 public void run() { 10 System.out.println(this.getName() + "開始執行," + (this.isDaemon() ? "我是守護執行緒" : "我是使用者執行緒")); 11 while (true) ; 12 } 13 } 14 15 public static void main(String[] args) { 16 T1 t1 = new T1("子執行緒1"); 17 t1.start(); 18 System.out.println("主執行緒結束"); 19 } 20 }

結果如圖:

 

可以看到主執行緒已經結束了,但是程式無法退出,原因:子執行緒1是使用者執行緒,內部有個死迴圈,一直處於執行狀態,無法結束。

再看下面的程式碼:

 1 public class Demo2 {
 2 
 3     public static class T1 extends Thread {
 4         public T1(String name) {
 5             super(name);
 6         }
 7 
 8         @Override
 9         public void run() {
10             System.out.println(this.getName() + "開始執行," + (this.isDaemon() ? "我是守護執行緒" : "我是使用者執行緒"));
11             while (true) {
12             }
13         }
14     }
15 
16     public static void main(String[] args) {
17         T1 t1 = new T1("子執行緒1");
18         t1.setDaemon(true);
19         t1.start();
20         System.out.println("主執行緒結束");
21     }
22 }

結果如圖:

程式可以正常結束了,程式碼中通過t1.setDaemon(true);將t1執行緒設定為守護執行緒,main方法所在的主執行緒執行完畢之後,程式就退出了。

結論:當程式中所有的使用者執行緒執行完畢之後,不管守護執行緒是否結束,系統都會自動退出。

 

場景二:設定守護執行緒,需要在start()方法之前進行

程式碼如下:

 1 import java.util.concurrent.TimeUnit;
 2 
 3 public class Demo3 {
 4 
 5     public static void main(String[] args) {
 6         Thread t1 = new Thread() {
 7             @Override
 8             public void run() {
 9                 try {
10                     TimeUnit.SECONDS.sleep(10);
11                 } catch (InterruptedException e) {
12                     e.printStackTrace();
13                 }
14             }
15         };
16         t1.start();
17         t1.setDaemon(true);
18     }
19 }

 

結果如下:

t1.setDaemon(true);是在t1的start()方法之後執行的,執行會報異常。

 

場景三、執行緒daemon的預設值

我們看一下建立執行緒原始碼,位於Thread類的init()方法中:

1 Thread parent = currentThread();
2 this.daemon = parent.isDaemon();

dameon的預設值為為父執行緒的daemon,也就是說,父執行緒如果為使用者執行緒,子執行緒預設也是使用者現場,父執行緒如果是守護執行緒,子執行緒預設也是守護執行緒。

示例程式碼:

 1 import java.util.concurrent.TimeUnit;
 2 
 3 public class Demo4 {
 4     public static class T1 extends Thread {
 5         public T1(String name) {
 6             super(name);
 7         }
 8 
 9         @Override
10         public void run() {
11             System.out.println(this.getName() + ".daemon:" + this.isDaemon());
12         }
13     }
14 
15     public static void main(String[] args) throws InterruptedException {
16 
17         System.out.println(Thread.currentThread().getName() + ".daemon:" + Thread.currentThread().isDaemon());
18 
19         T1 t1 = new T1("t1");
20         t1.start();
21 
22         Thread t2 = new Thread() {
23             @Override
24             public void run() {
25                 System.out.println(this.getName() + ".daemon:" + this.isDaemon());
26                 T1 t3 = new T1("t3");
27                 t3.start();
28             }
29         };
30 
31         t2.setName("t2");
32         t2.setDaemon(true);
33         t2.start();
34 
35         TimeUnit.SECONDS.sleep(2);
36     }
37 }

執行程式碼,輸出:

t1是由主執行緒(main方法所在的執行緒)建立的,main執行緒是t1的父執行緒,所以t1.daemon為false,說明t1是使用者執行緒。

t2執行緒呼叫了setDaemon(true);將其設為守護執行緒,t3是由t2建立的,所以t3預設執行緒型別和t2一樣,t2.daemon為true。

 

守護執行緒適用場景

  針對於守護執行緒的特點,java 守護執行緒通常可用於開發一些為其它使用者執行緒服務的功能。比如說心跳檢測,事件監聽等。Java 中最有名的守護程序當屬GC(垃圾回收)

 

 

總結

  1. java中的執行緒分為使用者執行緒守護執行緒
  2. 程式中的所有的使用者執行緒結束之後,不管守護執行緒處於什麼狀態,java虛擬機器都會自動退出
  3. 呼叫執行緒的例項方法setDaemon()來設定執行緒是否是守護執行緒
  4. setDaemon()方法必須線上程的start()方法之前呼叫,在後面呼叫會報異常,並且不起效
  5. 執行緒的daemon預設值和其父執行緒一樣