1. 程式人生 > >淺析Thread.join()

淺析Thread.join()

線程 edi xtend `` cond 通過 parent zed lar

概要

本文分為三部分對 hread.join() 進行分析:

1. join() 的示例和作用

2. join() 源碼分析

3. 對網上其他分析 join() 的文章提出疑問

1. join() 的示例和作用

// 主線程
public class Parent extends Thread {
    public void run() {
        Child child = new Child();
        child .start();
        child .join();
        // ...
    }
}
// 子線程 public class Child extends
Thread { public void run() { // ... } }

上面代碼中有兩個類:Parent(主線程類),Child(子線程類)。

在 Parent.run() 中,通過 new Child() 新建 child 子線程(此時 child 處於 NEW 狀態),然後調用 child.start()(child 轉換為 RUNNABLE 狀態),再調用 child.join()。

在 Parent 調用 child.join() 後,child 子線程正常運行,Parant 主線程會等待 child 子線程結束後再繼續運行。

join() 的作用:讓主線程等待子線程結束之後才能繼續運行。

Java 7 Concurrency Cookbook 中的描述:

Waiting for the finalization of a thread

In some situations, we will have to wait for the finalization of a thread. For example, we may have a program that will begin initializing the resources it needs before proceeding with the rest of the execution. We can run the initialization tasks as threads and wait for its finalization before continuing with the rest of the program. For this purpose, we can use the join() method of the Thread class. When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.

2. join() 源碼分析

以下是 JDK 8 中對 join() 的源碼:

 1 public final void join() throws InterruptedException {
 2     join(0);
 3 }
 4 
 5 public final synchronized void join(long millis)
 6 throws InterruptedException {
 7     long base = System.currentTimeMillis();
 8     long now = 0;
 9 
10     if (millis < 0) {
11         throw new IllegalArgumentException("timeout value is negative");
12     }
13 
14     if (millis == 0) {
15         while (isAlive()) {
16             wait(0);
17         }
18     } else {
19         while (isAlive()) {
20             long delay = millis - now;
21             if (delay <= 0) {
22                 break;
23             }
24             wait(delay);
25             now = System.currentTimeMillis() - base;
26         }
27     }
28 }
29 
30 public final synchronized void join(long millis, int nanos)
31 throws InterruptedException {
32 
33     if (millis < 0) {
34         throw new IllegalArgumentException("timeout value is negative");
35     }
36 
37     if (nanos < 0 || nanos > 999999) {
38         throw new IllegalArgumentException(
39                             "nanosecond timeout value out of range");
40     }
41 
42     if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
43         millis++;
44     }
45 
46     join(millis);
47 }

join() 有三個版本:

public final void join();
public final synchronized void join(long millis);
public final synchronized void join(long millis, int nanos);
 

其中 join() 和 join(long millis, int nanos) 最後都調用了 join(long millis)。

帶參數的 join() 都為 synchronized method。

join() 說明

join() 調用了 join(0),從源碼可以看到 join(0) 不斷檢查線程(join() 所屬的線程實例,非調用線程)是否是 Active。

以本文開頭的示例為例,Parent 調用 child.join(),child.join() 再調用 child.join(0) (此時 Parent 會獲得 child 實例作為鎖,其他線程可以進入 child.join() ,但不可以進入 child.join(0), 因為沒有獲得鎖),child.join(0) 會不斷地檢查 child 線程是否是 Active。

如果是 Active,則不斷地調用 child.wait(0)(此時 Parent 會釋放 child 實例鎖,其他線程可以競爭鎖並進入 child.join(0))。我們可以得知,Parent 線程在不斷地對 child.wait(0) 入棧和出棧。

一旦 child 線程不為 Active (狀態為 TERMINATED), child.join(0) 會直接返回到 child.join(), child.join() 會直接返回到 Parent 主線程,Parent 主線程就可以繼續運行下去了。

示意圖:

Parent-------new Child()----RUNNING-----child.start()-----RUNNING----child.join()*****TIMED_WAITING******-----RUNNING-------TERMINATED
	      |                |                                   |
		  |                |                                   |
		  |                |                                   |
		  |                |                                   |
		Child`````````NEW``````````Child-------------------------RUNNING---------------------TERMINATED

3. 對網上其他分析 join() 的文章提出疑問

網上其他文章描述:

子線程結束之後,會“喚醒”主線程,主線程重新獲取cpu執行權,繼續運行。

“喚醒”令人誤解。其實是主線程調用方法不斷去檢查子線程的狀態, 這是個主動的動作。

淺析Thread.join()