程式扎記: [ Java設計模式 ] 多線程設計模式 : Single Thread Execution Pattern

標籤

2010年9月20日 星期一

[ Java設計模式 ] 多線程設計模式 : Single Thread Execution Pattern

前言 :
考慮有一條獨木橋, 因為橋身非常細故一次只能容納一個人通過. 這一章要學習的是 Single Thread Execution Pattern. 指 " 同一時間以一個線程執行 " 的意思. 而這個 Single Thread Execution Pattern 將會是多線程程序設計的基礎. 而Single Threaded Execution 有時候也稱 Critical Section 或 Critical Region. 前者是把焦點放在 運行的線程上, 而後者是把視線放在執行的範圍 (橋身) 上.


Pattern 參予者 :
* Shared Resource (共享資源) :
Shared Resource 是可由多個線程訪問的類別, 並在在這個類別會擁有一些方法. 這些方法以多線程的角度來看可以分為 :
> Safe Method - 從多線程同時調用也不會發生問題
> Unsafe Method - 由多線程調用會出問題, 而需要加以防護.

在Single Thread Execution Pattern 中, 我們使用 synchronized 限制在Unsafe Method 同時間只能有一個線程可以調用它. 而這個必須只讓單線程執行的範圍, 我們稱為 臨界區 (Critical section).

使用時機 :
* 多線程環境
* 數據會被多個線程訪問時
* Shared Resource 狀態會改變時

使用範例 :
這裡要寫的程序是要模擬三個人(UserThread)頻繁通過一個只允許一個人通過的門 (Gate). 當人通過時, 這個程序會在計數器遞增通過人數, 並記錄通過人的姓名與出生地.
* Main 類
Main 類用來創建一個門 (Gate) 並讓三個人 (UserThread) 不斷的通過. 創建 Gate 類物件並將之作為參數丟到 UserThread 類的建構子做為參數.
下面會有三個人會通過這個人 :
>Alice - Alaska
>Bobby - Brazil
>Chris - Canada
為了便於對應兩者之間的關係, 故意將姓名與出生地的第一個字元取為相同.
代碼 :
  1. package dp.thread.ch1;  
  2.   
  3. import dp.thread.ch1.proto.IGate;  
  4.   
  5. public class Main {  
  6.   
  7.     /** 
  8.      * Chap1 : Single Threaded Execution - 能通過這座橋的一次只有一個人 
  9.      * @param args 
  10.      */  
  11.     public static void main(String[] args) {  
  12.         /*Testing Not Thread Safe Gate*/  
  13.         //System.out.println("Test Not Thread Safe Gate, hit CTRL+C to exit!");  
  14.         //IGate gate = new GateNTS();         
  15.           
  16.         /*Testing Thread Safe Gate*/  
  17.         System.out.println("TestThread Safe Gate, hit CTRL+C to exit!");  
  18.         IGate gate = new Gate();      
  19.           
  20.         new UserThread(gate,"Alice","Alaska",1000).start();  
  21.         new UserThread(gate,"Boddy","Brazil",1000).start();  
  22.         new UserThread(gate,"Chris","Canada",1000).start();           
  23.     }  
  24. }  


* Gate 類 :
Gate 表示人要通過的門. counter 字段表示目前已通過門的人數統計, name字段表示當前通過門的人的姓名, 而address 字段則是當前通過門的人的出生地. pass 是穿越門的方法. 會進行 counter 遞增與紀錄當前通過人的相關訊息. check 方法則是用來檢查現在門的狀態, 若當前的姓名與出生地的第一個字元不同, 則打印 "**** BROKEN ****" 訊息. 而這個Gate 類即是前面所提到參予者的 Shared Resource.

代碼 : (IGate)
  1. package dp.thread.ch1.proto;  
  2.   
  3. public interface IGate {      
  4.     public void pass(String name,String a);  
  5. }  
代碼 : (GateNTS 非線程安全的Gate)
  1. package dp.thread.ch1;  
  2.   
  3. import dp.thread.ch1.proto.*;  
  4.   
  5. public class GateNTS implements IGate{  
  6.     private int counter = 0;  
  7.     private String name = "Nobody";  
  8.     private String address = "Nowhere";  
  9.       
  10.     public void pass(String n,String a){  
  11.         this.counter++;  
  12.         this.name = n;  
  13.         this.address = a;  
  14.         check();  
  15.     }  
  16.       
  17.     public String toString(){  
  18.         return "No. "+counter+": "+name+","+address;  
  19.     }  
  20.       
  21.     private void check(){  
  22.         if(name.charAt(0) != address.charAt(0)){  
  23.             System.out.println("*****BROKEN*****"+toString());  
  24.         }  
  25.     }             
  26. }  
代碼 : (Gate 線程安全的Gate)
  1. package dp.thread.ch1;  
  2.   
  3. import dp.thread.ch1.proto.IGate;  
  4.   
  5. public class Gate implements IGate {  
  6.     private int counter = 0;  
  7.     private String name = "Nobody";  
  8.     private String address = "Nowhere";  
  9.   
  10.     @Override  
  11.     public synchronized void pass(String n, String a) {  
  12.         this.counter++;  
  13.         this.name = n;  
  14.         this.address = a;  
  15.         check();  
  16.     }  
  17.       
  18.     public synchronized String toString(){  
  19.         return "No. "+counter+": "+name+","+address;  
  20.     }  
  21.       
  22.     private void check(){  
  23.         if(name.charAt(0) != address.charAt(0)){  
  24.             System.out.println("*****BROKEN*****"+toString());  
  25.         }  
  26.     }  
  27. }  

* UserThread 類
UserThread 類表示不斷穿過門的行人. 這個類別聲明成Thread 類的子類. gate字段表示要通過的門, myname 字段表示姓名, myaddress字段 表示出生地 而 pt字段 表示要通過門的次數. run 方法 一開始會顯示自己的姓名 與 BEGIN 字樣. 接著則會以設定的次數通過gate (呼叫IGate.pass).
代碼 :
  1. package dp.thread.ch1;  
  2.   
  3. import dp.thread.ch1.proto.IGate;  
  4.   
  5. public class UserThread extends Thread{  
  6.     private final IGate gate;  
  7.     private final String myname;  
  8.     private final String myaddress;  
  9.     private int pt = 1;  
  10.       
  11.     public UserThread(IGate g,String n,String a,int p){  
  12.         this.gate = g;  
  13.         this.myaddress = a;  
  14.         this.myname = n;  
  15.         this.pt = p;  
  16.     }  
  17.       
  18.     public void run(){  
  19.         System.out.println(myname + " BEGIN("+pt+")");  
  20.         for(int i=0;i
  21.             gate.pass(myname, myaddress);  
  22.         }  
  23.     }     
  24. }  
執行結果 :
1. 如果你是以非線程安全的Gate (GateNTS) 去跑, 則可能出現如下結果 :
Test Not Thread Safe Gate, hit CTRL+C to exit!
Alice BEGIN(1000)
Boddy BEGIN(1000)
*****BROKEN*****No. 569: Alice,Alaska
*****BROKEN*****No. 788: Alice,Alaska
*****BROKEN*****No. 955: Boddy,Brazil
*****BROKEN*****No. 1119: Boddy,Brazil
Chris BEGIN(1000)

2. 如果你是以線程安全的Gate 去跑, 則不會發生BROKEN.
TestThread Safe Gate, hit CTRL+C to exit!
Alice BEGIN(1000)
Boddy BEGIN(1000)
Chris BEGIN(1000)

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!