Java併發程式設計中,其中一個難點是對執行緒生命週期的理解,和多種執行緒控制方法、執行緒溝通方法的靈活運用。這些方法和概念之間彼此聯絡緊密,共同構成了Java併發程式設計基石之一。

Java執行緒的生命週期

Java執行緒類定義了New、Runnable、Running Man、Blocked和Dead五種狀態。

New

當初始化了一個執行緒物件之後,執行緒就進入了New的狀態。此時JVM會為其分配記憶體、初始化成員變數的值,跟一般的物件一樣。

Runnable

當呼叫執行緒物件的start方法之後,就進入Runnable狀態。JVM會為其建立虛擬機器棧程式計數器。此時僅表面執行緒可以開始執行,但何時執行取決於JVM的執行緒排程器。

Running

當執行緒獲取到CPU資源的時候,就進入Running狀態執行執行緒方法了。如果執行緒數大於多處理器的數目,會存在多個執行緒輪換,儘管多個處理器會同時並行處理幾個執行緒。

執行緒排程的細節取決於底層平臺,當Running的執行緒呼叫其yield方法或失去CPU資源的時候,即回到Runnable狀態

Blocked

當發生如下情況,執行緒會被阻塞/重新進入Runnable狀態:

1. 執行緒呼叫sleep方法                ===>    sleep方法經過指定時間

2. 執行緒呼叫了一個阻塞式IO              ===>    呼叫的阻塞式IO方法返回 

3. 試圖獲取一個正被使用的同步鎖           ===>    成功獲取同步鎖

4. 等待notify                     ===>    其它執行緒發出了notify

5. 執行緒呼叫suspend方法(容易導致死鎖,不建議使用) ===>     被呼叫了resume方法

Dead

當發生如下情況,執行緒結束

1. 執行緒執行體完成

2. 丟擲未捕獲的異常或錯誤

3. 直接呼叫stop方法(容易導致死鎖,不建議使用)

可通過呼叫執行緒物件的isAlive方法,如果處於新建和死亡狀態會返回false

執行緒管理

常用的執行緒管理包括設定後臺執行緒、設定優先順序、join、sleep和yield

設定後臺執行緒

setDaemon(true):設定為後臺執行緒

isDaemon():用於判斷指定執行緒是否為後臺執行緒

設定優先順序

setPriority(int priority):設定優先順序

getPriority():獲取優先順序

 1 public class ThreadPriority {
 2 
 3     public static void main(String[] args) {
 4         Thread t1 = new Thread(()->
 5         {
 6             while(true) {
 7                 System.out.println("t11111");
 8             }
 9         }, "t1");
10         t1.setPriority(Thread.NORM_PRIORITY);
11         
12         Thread t2 = new Thread(()->{
13             while (true) {
14                 System.out.println("t22222");
15             }
16         });
17         t2.setPriority(10);
18 
19         t1.start();
20         t2.start();
21     }
22 
23 }
View Code

join

join執行緒可以理解為把一個問題分為若干小問題由不同的執行緒處理,其它執行緒處理過程中,呼叫join方法的執行緒處於阻塞狀態,在其它執行緒處理完畢後,再回到執行狀態的概念。

 1 public class ThreadJoin {
 2     
 3     private static void shortSleep() {
 4         try {
 5             TimeUnit.SECONDS.sleep(1);
 6         } catch (InterruptedException e) {
 7             e.printStackTrace();
 8         }
 9     }
10     
11     private static Thread create(int seq) {
12         return new Thread(() -> {
13             for (int i = 0; i < 10; i++) {
14                 System.out.println(Thread.currentThread().getName() + "#" + i);
15                 shortSleep();
16             }
17         }, String.valueOf(seq));
18     }
19 
20     public static void main(String[] args) throws InterruptedException {
21         
22         List<Thread> threads = IntStream.range(1, 3).mapToObj(ThreadJoin::create).collect(Collectors.toList());
23         
24         threads.forEach(Thread::start);
25         //main執行緒呼叫join方法, 會進入阻塞, 等其它執行緒完成了再行繼續
26         for(Thread thread : threads) {
27             thread.join();
28         }
29         
30         for(int i = 0; i < 10; i++) {
31             System.out.println(Thread.currentThread().getName() + "#" + i);
32             shortSleep();
33         }
34     }
35 }
View Code

sleep

Thread類的靜態方法,用於暫停執行緒的執行。一般建議使用1.5後新增的TimeUnit類來更好的暫停執行緒

 1 public class ThreadSleep {
 2     
 3     private static void sleep(int ms) {
 4         try {
 5             TimeUnit.SECONDS.sleep(ms);
 6         } catch (InterruptedException e) {
 7             e.printStackTrace();
 8         }
 9     }
10 
11     public static void main(String[] args) {
12 
13         new Thread(() -> 
14         {
15             long startTime = System.currentTimeMillis();
16             sleep(2);
17             long endTime = System.currentTimeMillis();
18             System.out.println(String.format("Total spend %d second", (endTime - startTime)));
19         }).start();
20         
21         /*
22          * Thread sleep times depends on your system
23          */
24         long startTime = System.currentTimeMillis();
25         sleep(3);
26         long endTime = System.currentTimeMillis();
27         System.out.println(String.format("Main thread total spend %d second", (endTime - startTime)));
28         
29     }
30 }
View Code

yield

與sleep方法不同,yield方法只是讓當前執行緒暫停一下,以便執行緒排程器操作執行緒排程。該方法不會讓執行緒進入阻塞

 1 public class ThreadYield {
 2     
 3     private static Thread create(int index) {
 4         try {
 5             TimeUnit.SECONDS.sleep(1);
 6         } catch (InterruptedException e) {
 7             e.printStackTrace();
 8         }
 9         return new Thread(()->
10         {
11             if (index == 0) {
12                 Thread.yield();
13             }
14             System.out.println(index);
15         });
16     }
17 
18     public static void main(String[] args) {
19         IntStream.range(0, 2).mapToObj(ThreadYield::create).forEach(Thread::start);
20     }
21 }
View Code