Multiple Threads 常見的問題與解決:執行的先後順序關係

Multiple Threads 常見的問題與解決:執行的先後順序關係

這篇文章主要用一個Java實例介紹 Multiple threads執行時遇到的問題與解決方式。執行的先後順序。

當Thread A 在執行的時候 Thread B 也在執行。同時執行時,就會發生一些非預期的狀況。例如, Thread A 必須要等 Thread B 執行完結果。才能繼續執行。

什麼情況會需要這樣等待呢? 例如,顧客 (Thread A)買完東西到櫃檯結帳,我們通常要等到結帳櫃台(Thread B)計算出總金額,才會付款離開。

這種情況,顧客 (Thread A)就必須刻意等待 結帳櫃台(Thread B)的執行結果,計算出總金額之後,顧客 (Thread A)才能離開。

如果顧客 (Thread A)不等待會怎樣呢? 結果變成顧客 (Thread A)不結帳,直接離開? 那麼 …..OOXX…

Java 範例:客戶(Thread Consumer)不等待結帳 (Thread Cashier)

首先我們用 Java 看一個範例,如果這兩個 thread 不等待會怎樣的結果

[pastacode lang=”java” message=”Java Multiple Threads” highlight=”9,10″ provider=”manual”]

public class ConsumerThread {
    public static void main(String[] args) throws InterruptedException{
    	CashierThread cashier = new CashierThread();
    	cashier.start();
 
        synchronized(cashier){
            System.out.println("I'm consumer, and waiting for cashier to checkout...");

            // if we don't wait for cashier to calculate the results of the total,  the total will be ZERO.
            //cashier.wait();


            System.out.println("Total is: " + cashier.total);
        }
    }
}
 
class CashierThread extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            total = total + 100;
            notify();
        }
    }
}

[/pastacode]

如果ConsumerThread不等待CashierThread,就會造成執行結果 Total 計算出來為 0 。

因為 CashierThread 還沒有計算完畢,還來不急把結果回傳。

 

Java範例:客戶(Thread Consumer)等待結帳 (Thread Cashier)

因此要解決這樣的問題就是要讓 ConsumerThread等待CashierThread把總金額計算完畢。

在 Java 中有幾個重要的程式語法要注意:

  • synchronized: 通知相關的 threads 需要互相協調
  • cashier.wait() : 等一下 cashier 的thread 執行完畢再繼續
  • notify(): cashier 執行完畢之後要通知一下,這樣 ConsumerThread才會知道,並且繼續接下來的執行動作。

 

[pastacode lang=”java” message=”” highlight=”” provider=”manual”]

public class ConsumerThread {
    public static void main(String[] args) throws InterruptedException{
    	CashierThread cashier = new CashierThread();
    	cashier.start();
 
        synchronized(cashier){
            System.out.println("I'm consumer, and waiting for cashier to checkout...");

            // if we don't wait for cashier to calculate the results of the total,  the total will be ZERO.
            cashier.wait();
            System.out.println("Total is: " + cashier.total);
        }
    }
}
 
class CashierThread extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            total = total + 100;
            notify();
        }
    }
}

[/pastacode]

用日常生活來解釋,我們希望達到的是:

用顧客 (Thread ConsumerThread)買完東西到櫃檯結帳,我們通常要等到結帳櫃台(Thread CashierThread)計算出總金額,才會付款離開。

 

首先客戶ConsumerThread會先到結帳櫃台,請櫃檯計算價格,因此櫃台啟動了 CashierThread

CashierThread cashier = new CashierThread();
cashier.start();

接著,客戶ConsumerThread需要做等待的動作,等待結帳櫃台。cashier.wait();

客戶有哪些動作是要跟結帳櫃台一起配合的呢?

synchronized(cashier){

…..

}

結帳退台CashierThread會開始執行,做結帳的動作。

結帳完成之後,由於客戶還在等待,因此要通知客戶,結帳完畢。 notify();

同樣的結帳櫃台有哪些動作是要跟客戶一起配合的呢?

synchronized(this){

…..

}

小結

當multiple thread 同時執行的時候,如果有些運算的結果必須等待另一個 thread 執行完畢,

例如,客戶等待結帳櫃檯計算總金額,

這種狀況下,我們就必須讓客戶先進行等待wait,接著櫃台計算完畢之後,在通知客戶 notify

另外,客戶與櫃檯之間有哪些動作需要相互協調。就會利用 synchronized。

當然,這是 Java 的語言特性。如果是其他程式語言在處理 multiple threads 時,也必須注意這樣的問題。

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *