Java 的多執行緒之等待其他執行緒執行完成,三匹馬的賽事

Java 的多執行緒之等待其他執行緒執行完成,三匹馬的賽事

三匹馬的賽事

 設計多執行緒 Threading 程式時,常需要等待其他執行緒的執行結果,收集資訊後再進一步處理,本文章延續上一篇文章:

Java 的多執行緒,以賽馬為例,如何繼承 Thread 與實作 Runnable 介面

 在上一篇文章中,以 main 執行緒為第一匹馬,另外以 Horse 執行緒為第二匹馬,現在想讓 main 方法專心處理產生執行緒與計算賽馬名次等工作,因此,改變為三匹馬都以 Horse 執行緒來執行,在 main 方法中產生三個 Horse 執行緒物件,利用 Thread 的方法 setName() 給予三匹馬執行緒名稱(h1, h2, h3),main 方法執行到最後時,印出「main 執行緒結束」:

threading

sleep 方法

 為了方便觀察 Horse 執行緒的執行結果,將原 for 迴圈改為讓執行緒睡覺 2 秒鐘,可使用 Thread 的方法 sleep(long millis),共 2000 毫秒,sleep 方法使用時必須以 try…catch 處理被意外中斷的例外 InterruptedException:

public class Horse extends Thread {
    public void run() {
        try {
            sleep(2000);
            System.out.println(getName() + "到達終點");
            } catch (InterrruptedException e) {
                System.out.println("被中斷了");
            }
        }
    }
} 

 執行 Racing3 類別的結果如下(每次結果有可能不一樣):

main執行緒結束
h1到達終點
h3到達終點
h2到達終點 

 發現一個問題了嗎?就是 main 執行緒是三匹馬執行緒的發起者,但它卻比三匹馬執行緒還要早結束,若是如此,我們將無法在三匹馬跑出結果時印出到達順序,如何能讓 main 執行緒等待三匹馬執行緒都結束後,才繼續往後執行呢?

 如果讓 main 執行緒進入睡眠一段時間,雖然可以在睡醒時進行後續的動作,但如果睡覺時間不夠準確,或是三匹馬的執行緒完成時間是未知時,sleep() 方法也就不適合用來等待其他「子執行緒」,因此,可以利用 join() 方法達到等待其他執行緒完成後,再進行後續的動作。

join() 方法-等待

 Thread 類別的方法 join(),可以用來等待該執行緒完成,使用 join() 方法時,和 sleep() 方法一樣,必須以 try…catch 處理被意外中斷的例外 InterruptedException,如下:

threading

 觀察執行結果可發現,main 執行緒會等待三匹馬執行緒都完成後,才會繼續第 21 行的執行:

h2到達終點 
h3到達終點 
h1到達終點 
main執行緒結束 

結算賽馬的名次

 瞭解如何利用 join() 方法來等待子執行緒之後,先訂定一個放置三匹馬的集合 rank,第一匹到達的馬可以進入到 rank 集合的第 1 個位置,第二匹到達的馬則進入到 rank 集合的第 2 的位置,以此類推。使用 java.util.ArrayList 做為集合類別,新增一類別 Racing4 來設計此功能:

import java.util.ArrayList;
import java.util.List;

public class Racing4 {
    public static void main(String[] args) {
        List<RankHorse> rank = new ArrayList<>();
        RankHorse h1 = new RankHorse(rank);
        RankHorse h2 = new RankHorse(rank);
        RankHorse h3 = new RankHorse(rank);
        h1.setName("h1");
        h2.setName("h2");
        h3.setName("h3");
        h1.start();
        h2.start();
        h3.start();
        try {
            h1.join();
            h2.join();
            h3.join();
        } catch (InterruptedException e) {
            System.out.println("執行緒被中斷");
        }
        System.out.println(rank);
        System.out.println("main執行緒結束");
    }
} 

 上述程式碼第 7 行,新增一個 List 物件 rank,而且以「一般化」限定 rank 集合裏只能放置 RankHorse 物件,第 8-10 行則利用 RankHorse 的建構子將 rank 物件傳遞給執行緒類別 RankHorse 內部使用,最後在第 24 行依序印出 rank 集合內的所有物件(三匹馬)。因此,我們還要依照先前 Horse 類別的模式,再設計 RankHorse 類別,使其擁有排名的功能,範例程式如下:

import java.util.List;

public class RankHorse extends Thread {
    List<RankHorse> rank;
    public RankHorse(List<RankHorse> rank){
        this.rank = rank;
    }

    @Override
    public void run() {
        try {
            sleep(2000);
            System.out.println(getName() + "到達終點");
            //放進rank中
            rank.add(this);
        } catch (InterruptedException e) {
            System.out.println(getName() + "被中斷了");
        }
    }
} 

 上述程式的第 4 行,定義了成員 rank,第 6-8 行則新增了建構子,以接收 Racing4 所傳來的 rank 物件,最後,等到此匹馬到達終點時,第 17 行將自己放入 rank 集合中,若此匹馬是第一名到達,將會放置在第一位。

Racing4 類別的執行結果如下,可以看到 rank 集合內依照到達順序的三匹馬:

h2到達終點
h3到達終點
h1到達終點
[Thread[h2,5,], Thread[h3,5,], Thread[h1,5,]]
main執行緒結束 

 集合內的每匹 RankHorse 的 h1, h2, h3 是執行緒的名稱,接著逗號後跟著該執行緒的優先權值,預設是 5,有關執行優先權之後再另以文章介紹。

版權聲明

本文章授權範圍僅限綠豆湯網站使用,除 Facebook 之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。

發佈留言