前言
在學習java中的併發的時候,很容易注意到一個函數join
,此函數的功能是等待指定的線程死亡或者經過指定的毫秒數,如果不指定毫秒數或者指定的毫秒數爲空,則一直等待直到指定的線程死亡。JDK源碼如下:
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
// 會檢測線程是否存活,如果線程已經死亡則不需要等待
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join()
就等價於join(0)
,join
函數的功能挺簡單,不過在學習查閱資料的過程中,發現在等待的過程中,會影響到其它線程的工作狀態,所以自己寫了一個小demo測試了一下,最後總結出了join
更深層次的含義。
測試join函數
在下面的小demo中,我寫了兩個測試的線程,分別開啓線程,觀察輸出。
public class JoinTest {
public static void main(String[] args) {
System.out.println("主線程開始執行");
Runnable r1 = () -> {
System.out.println("線程1開始執行");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println("線程1結束執行");
};
Runnable r2 = () -> {
System.out.println("線程2開始執行");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
System.out.println("線程2結束執行");
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
System.out.println("主線程結束執行");
}
}
----------輸出結果----------
主線程開始執行
主線程結束執行
線程1開始執行
線程2開始執行
線程2結束執行
線程1結束執行
----------分析----------
主線程開始->線程1、線程2隨機某個時點開始執行
(可能在主線程結束後,線程1、2結束時間點由開始時間和運行時長決定)->主線程結束執行
對於上面的demo,可以很明顯的看出,在主線程執行的時候,開啓了兩個新線程,但是兩個新線程的開始執行時間是隨機的,而結束時間我人爲設置了線程2會執行得更快。
public class JoinTest {
public static void main(String[] args) {
System.out.println("主線程開始執行");
......
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
}
t2.start();
System.out.println("主線程結束執行");
}
}
----------輸出結果----------
主線程開始執行
線程1開始執行
線程1結束執行
主線程結束執行
線程2開始執行
線程2結束執行
----------分析----------
主線程開始->線程1隨機某個時點開始執行->線程1結束執行->
主線程結束執行->線程2隨機某個時點開始執行(可能在主線程結束前)->線程2結束執行
當主線程調用線程1的join()方法,主線程進入等待狀態,直到線程1運行結束,主線程才由等待狀態轉爲可執行狀態,所以一定是在線程1結束後主線程才能結束執行。
public class JoinTest {
public static void main(String[] args) {
System.out.println("主線程開始執行");
......
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
}
System.out.println("主線程結束執行");
}
}
----------輸出結果----------
主線程開始執行
線程1開始執行
線程2開始執行
線程2結束執行
主線程結束執行
線程1結束執行
----------分析----------
主線程開始->線程1、線程2隨機某個時點開始執行
(可能在主線程結束後,線程1、2結束時間點由開始時間和運行時長決定)->
線程2結束執行->主線程結束執行
同理,當主線程調用線程2的join()方法,主線程進入等待狀態,直到線程2運行結束,主線程才由等待狀態轉爲可執行狀態,所以一定是在線程2結束後主線程才能結束執行。
總結
在A
線程中,使用B.join()
函數後會等待指定的線程死亡,然後繼續運行,如果有其它線程C
其執行規則不變。