Java基礎填坑日誌(2)——Thread.join()方法使用分析

前言

在學習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隨機某個時點開始執行
(可能在主線程結束後,線程12結束時間點由開始時間和運行時長決定)->主線程結束執行

對於上面的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隨機某個時點開始執行
(可能在主線程結束後,線程12結束時間點由開始時間和運行時長決定)->
線程2結束執行->主線程結束執行

同理,當主線程調用線程2的join()方法,主線程進入等待狀態,直到線程2運行結束,主線程才由等待狀態轉爲可執行狀態,所以一定是在線程2結束後主線程才能結束執行。

總結

A線程中,使用B.join()函數後會等待指定的線程死亡,然後繼續運行,如果有其它線程C其執行規則不變。

參考

多線程中join()
Thread源碼分析之join方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章