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 時,也必須注意這樣的問題。