1. 程式人生 > 其它 >多執行緒 join 方法

多執行緒 join 方法

技術標籤:併發多執行緒

多執行緒 join 方法

1.前言
本節對 join 方法進行深入的剖析,主要內容點如下:

  • 瞭解 join 方法的作用,初步的理解 join 方法的使用帶來的效果是學習本節內容的基礎;
  • 瞭解 join 方法異常處理,我們在使用 join 方法是,需要對 join 方法進行有效的異常處理;
  • 通過匿名內部類建立Thread 是我們本節程式碼示例所使用的的方式,對這種方式的掌握在後續工作中非常重要;
  • 掌握 join 方法如何使用,這是本節的重點內容,也是本節的核心內容;
  • 掌握帶引數的 join 方法使用,在後續開發過程中,如果對介面的最大返回時間有要求的話,某些情況下會用到帶引數的 join
    方法,次重點內容。

2. join 方法的作用
方法定義: 多執行緒環境下,如果需要確保某一執行緒執行完畢後才可繼續執行後續的程式碼,就可以通過使用 join 方法完成這一需求設計。

在專案實踐中經常會遇到一個場景,就是需要等待某幾件事情完成後主執行緒才能繼續往下執行, 比如多個執行緒載入資源, 需要等待多個執行緒全部載入完畢再彙總處理。

Thread 類中有一個 join 方法就可以做這個事情,join 方法是 Thread 類直接提供的。join 是無參且返回值為 void 的方法。
在這裡插入圖片描述
如上圖所示,假如有 3 個執行緒執行邏輯,執行緒 1 需要執行5秒鐘,執行緒 2 需要執行10 秒鐘,執行緒 3 需要執行 8 秒鐘。 如果我們的開發需求是:必須等 3 條執行緒都完成執行之後再進行後續的程式碼處理,這時候我們就需要使用到 join 方法。

使用 join 方法後:

第 5 秒鐘: 執行緒 1 執行完畢;執行緒 2 執行了一半; 執行緒 3 還差 3 秒執行完畢;
第 8 秒鐘: 執行緒 1 等待了 3 秒; 執行緒 3 執行完畢; 執行緒 2 還差 2 秒執行完畢;
第10 秒鐘: 執行緒 1 等待了 5 秒; 執行緒 3 等待了 2 秒;執行緒 2 執行完畢;
從執行緒 2 執行結束的那一刻:三條執行緒同時進行後續程式碼的執行。

這就是 join 方法的作用與解釋。
3. join 方法異常處理
join 方法是 Thread 類中的方法,為了瞭解該方法的異常處理,我們先來簡要的看下 join 方法的 JDK 原始碼:

public
final void join() throws InterruptedException { join(0); }

從原始碼中我們可以看到, join 方法丟擲了異常:
throws InterruptedException
所以,我們在使用 join 方法的時候,需要對 join 方法的呼叫進行 try catch 處理或者從方法級別進行異常的丟擲。
try-catch 處理示例:

public class DemoTest implements Runnable{
    @Override
    public void run() {
        System.out.println("執行緒:"+Thread.currentThread()+" 正在執行...");
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new DemoTest());
        t1. start();
        try {
            t1.join();
        } catch (InterruptedException e) {
            // 異常捕捉處理
        }
    }
}

throws 異常處理示例:

public class DemoTest implements Runnable throws InterruptedException {
    @Override
    public void run() {...}
    public static void main(String[] args) {
        Thread t1 = new Thread(new DemoTest());
        t1. start();
        t1.join();
    }
}

4. join 方法如何使用
為了更好的瞭解 join 方法的使用,我們首先來設計一個使用的場景。
場景設計:

執行緒 1 : 執行時間 5 秒鐘;
執行緒 2 : 執行時間 10 秒鐘;
執行緒 3: 執行 8 秒鐘。
需求: 我們需要等 3 個執行緒都執行完畢後,再進行後續程式碼的執行。3 個執行緒執行完畢後,請列印執行時間。

期望結果: 10 秒執行時間。
看到這個是不是似曾相識呢? 這就是我們本節第 2 知識點所舉出的示例,現在我們來進行程式碼實現和驗證,體會 join 方法的使用。
例項:

public class DemoTest{
    public static void main(String[] args) throws InterruptedException {
        Thread threadOne = new Thread(new Runnable() { //執行緒 1
            @Override
            public void run() {
                try {
                    Thread.sleep (5000 ); //執行緒 1 休眠 5 秒鐘
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println ("執行緒 1 休眠 5 秒鐘,執行完畢。");
            }
        });

        Thread threadTwo = new Thread(new Runnable() { //執行緒 2
            ...
                    Thread.sleep (10000 ); //執行緒 2 修眠 10 秒鐘
            ...
                System.out.println ("執行緒 2 修眠 10 秒鐘,執行完畢。");
            }
        });

        Thread threadThree = new Thread(new Runnable() {//執行緒 3
            ...
                    Thread.sleep (8000 ); //執行緒 3 修眠 8 秒鐘
            ...
                System.out.println ("執行緒 3 修眠 8 秒鐘,執行完畢。");
            }
        });

        Long startTime = System.currentTimeMillis();
        threadOne. start();threadTwo. start();threadThree. start();
        System.out.println("等待三個執行緒全部執行完畢再繼續向下執行,我要使用 join 方法了。");
        threadOne.join(); //執行緒 1 呼叫 join 方法
        threadTwo.join(); //執行緒 2 呼叫 join 方法
        threadThree.join(); //執行緒 3 呼叫 join 方法
        Long endTime = System.currentTimeMillis();
        System.out.println("三個執行緒都執行完畢了,共用時: "+ (endTime - startTime) + "毫秒");
    }
}

執行結果驗證:

等待三個執行緒全部執行完畢再繼續向下執行,我要使用 join 方法了。
執行緒 1 休眠 5 秒鐘,執行完畢。
執行緒 3 修眠 8 秒鐘,執行完畢。
執行緒 2 修眠 10 秒鐘,執行完畢。
三個執行緒都執行完畢了,共用時: 10002毫秒

從執行的結果來看,與我們對 join 方法的理解和分析完全相符。
5. 帶引數的 join 方法使用
除了無參的 join 方法以外, Thread 類還提供了有參 join 方法如下:

public final synchronized void join(long millis)
    throws InterruptedException

該方法的引數 long millis 代表的是毫秒時間。

方法作用描述:等待 millis 毫秒終止執行緒,假如這段時間內該執行緒還沒執行完,也不會再繼續等待。
結合上一個知識點的程式碼,我們都是呼叫的無參 join 方法,現在對上一個知識點程式碼進行如下調整:

threadOne.join(); //執行緒 1 呼叫 join 方法
threadTwo.join(3000); //執行緒 2 呼叫 join 方法
threadThree.join(); //執行緒 3 呼叫 join 方法


從程式碼中我們看到,執行緒 2 使用 join 方法 3000 毫秒的等待時間,如果 3000 毫毛後,執行緒 2 還未執行完畢,那麼主執行緒則放棄等待執行緒 2,只關心執行緒 1 和執行緒 3。
我們來看下執行結果:

等待三個執行緒全部執行完畢再繼續向下執行,我要使用 join 方法了。
執行緒 1 休眠 5 秒鐘,執行完畢。
執行緒 3 修眠 8 秒鐘,執行完畢。
三個執行緒都執行完畢了,共用時: 8000毫秒
執行緒 2 修眠 10 秒鐘,執行完畢。


從執行結果來看, 總用時 8000 毫秒,因為執行緒 2 被放棄等待了,所以只考慮執行緒 1 和執行緒 3 的執行時間即可。

6. 小結
在實際的開發場景中,經常會設計到對 join 方法的使用,無參方法使用更加常見,瞭解 join 方法的使用非常重要。

本節重中之重,就是掌握 join 方法的使用,join 方法在後續的開發工作中非常關鍵,很多情況下都會有所使用。