在實務中實作執行緒功能時,常需要管理這些產生的執行緒,如隊列與防止不同執行緒同時去存取到一個資源。本文章延續自以下幾篇文章:
執行緒的隊列(queue)
當執行緒的sleep()或yeild()方法被呼叫時,會暫停目前的工作,並進入執行緒的隊列(ready queue)排隊等待下一次CPU的執行單位。
使用sleep( t )會暫停目前工作t毫秒,並進入隊列中等待執行,使用yield()方法則會讓出目前CPU的執行時間,直接進入隊列等待執行。
資源的鎖定
設計多執行緒程式時需考慮到避免多個執行緒存取同一個資源,例如不同執行緒同時間去開啟同一個檔案,Java語言利用「同步方法(method-level)」與「同步區塊(block-level)」這兩個方式,提供開發人員使用,以避免此問題的發生。
同步方法
若為一個類別的方法加上synchronized修飾字,則會限定在同一時間,只能由此一個物件使用這個方法。假設有個繼承了Thread類別的魔法師Wizard類別,它擁有一個發出雷電的方法thunder(),並持續兩秒鐘的時間,依照遊戲的規定,同一時間只能有一個魔法師使出雷電,假若目前場上有三個魔法師,Wizard類別的設計如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 03 public class Wizard extends Thread { 04 public void run() { 05 thunder(); 06 } 07 08 public void thunder(){ 09 System.out.println("THUNDER!!"); 10 try { 11 sleep(2000); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 System.out.println("END"); 16 } 17 18 public static void main(String[] args) { 19 Wizard wizard = new Wizard(); 20 Thread thr1 = new Thread(wizard); 21 thr1.start(); 22 Thread thr2 = new Thread(wizard); 23 thr2.start(); 24 } 25 } |
上述的main方法中產生兩個魔法師執行緒,並執行它們,在它們的run()方法中皆馬上呼叫thunder()方法,使用雷電絕招,執行結果如下:
1 2 3 4 | THUNDER!! THUNDER!! END END |
這樣的結果違背了遊戲的規定,同時間只能有一個執行緒使用thunder(),要達到這個功能就需要同步方法(synchronized method),在thunder方法前加上「synchronized」修飾字,使其成為同步方法。同步方法被限定只能有一個物件單獨使用它,另一個執行緒就必需等待前一個執行完畢後才能取得執行權,thunder()方法修改如下:
1 2 | public synchronized void thunder(){ ...(省略) |
再執行的結果,成功地限定同一時間只能讓一個執行緒執行雷電絕招:
1 2 3 4 | THUNDER!! END THUNDER!! END |
同步區塊
上述的同步方法可以達成大部份的需求,但是如果必需同步化的方法是由父類別繼承而來而無法更改,或是無法修改該方法為synchronized時,可以使用「同步區塊(synchronized block)」,限定只能由單一執行緒能執行某段程式碼,與同步方法不同的是,同步區塊必需指定一個需要同步的物件,同步區塊的規格如下圖:
1 2 3 4 | synchronized( 物件 ){ 程式碼 程式碼 } |
將上一個魔法師類別稍作修改,成為Wizard2類別,同樣的遊戲規定,但取消thunder()方法,而將其工作移至run()方法中,並為其加入同步區塊的設計:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 03 public class Wizard2 extends Thread { 04 public void run() { 05 synchronized (this) { 06 System.out.println("THUNDER!!"); 07 try { 08 sleep(2000); 09 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 System.out.println("END"); 13 } 14 } 15 16 public static void main(String[] args) { 17 Wizard2 wizard = new Wizard2(); 18 Thread thr1 = new Thread(wizard); 19 thr1.start(); 20 Thread thr2 = new Thread(wizard); 21 thr2.start(); 22 } 23 } |
Wizard2類別的執行結果,可以達到與同步方法相同的效果:
1 2 3 4 | THUNDER!! END THUNDER!! END |
版權聲明
本文章授權範圍僅限綠豆湯網站使用,除Facebook之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。
講得很詳細,我有一個問題想問你
我今天就是要他執行時發生錯誤,我指的是平常都正常,突然就發生某方面的錯誤
會有什麼情況才會發生??