程式扎記: [ Java設計模式 ] 多線程設計模式 : Guarded Suspension Pattern (要等到我準備好喔)

標籤

2010年9月19日 星期日

[ Java設計模式 ] 多線程設計模式 : Guarded Suspension Pattern (要等到我準備好喔)

前言 : 

在家裡正當換衣服的時候, 忽然客廳門鈴響了, 原來是郵差送信來, 但衣服換到一半不能開門, 所以就先說出 "請等我一下", 請郵差在外面等候. 等換完衣服在把門打開.本章要學習的是Guarded Suspension Pattern. guarded 是 "被保護著" 或 "被防衛著" 的意思. suspension 則是 "暫停" 的意思. 當現在不適合馬上執行某個操作時, 就想要要求該線程等待, 這就是 Guarded Suspension Pattern. Guarded Suspension Pattern 會要求線程等候, 以保障實例的正常工作. 這個Pattern 還有 guarded wait, spin lock 等別稱.

示意圖 :

Pattern參與者 : 
* GuardedObject (被守護的對象) : 
GuardedObject 是一個擁有被防衛的方法的類. 當線程執行 guardedMethod時, 只要滿足警戒條件便會馬上執行. 但若警戒條件不成立, 就要開始等候. 警戒條件的成立與否會隨 GuardedObject 的狀態進行變化. 在Java 語言中, 是使用 while 語句與 wait 方法來實現 guardedMethod 的. 而使用 notify/notifyAll 方法來實現 stateChangingMethod. 在後面的範例會有示範.


使用範例 : 
* Request 類 : 
Request 類是用來表示請求. 雖然說是請求, 但這裡不過是用來作為ClientThread 傳遞給 ServerThread 的實例, 所以沒有特殊處理. Request 只是一個存放名稱 (name 字段) 的類而已. 
代碼 : 
  1. package dp.thread.ch3;  
  2.   
  3. public class Request {  
  4.     private final String name;  
  5.       
  6.     public Request(String n){  
  7.         this.name = n;  
  8.     }  
  9.   
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.       
  14.     public String toString(){  
  15.         return "[ Request "+name+" ]";  
  16.     }  
  17. }  

* RequestQueue 類 : 
RequestQueue 類是用來依次存放請求的類. 這個類擁有 getRequest 與 putRequest 兩個方法 : 
> getRequest 方法 
這個方法會從RequestQueue 裡存放的請求中, 將最早傳入的請求做為返回值 (類似Queue或是FIFO). 如果沒有任何一個請求, 就等待其它線程 putRequest.

> putRequest 方法 
使用 putRequest 方法可以增加一個請求, 當線程想要在 RequestQueue 放入一個 Request 實例, 就會調用這個方法.

這裡要注意的地方有 : 
@. getRequest, putRequest 都是 synchronized 方法;
@. getRequest 有 while 語句來判斷防衛條件使否滿足;
@. 在 while 塊中 會使用 wait 來暫停線程.
@. 在 while 語句後, 才會調用目的操作;
@. 在putRequest 裡, 要調用notifyAll 或 notify.

代碼 : 
  1. package dp.thread.ch3;  
  2.   
  3. import java.util.*;  
  4.   
  5. public class RequestQueue {  
  6.     private final LinkedList queue = new LinkedList();  
  7.       
  8.     public synchronized Request getRequest(){  
  9.         while(queue.size() <= 0){  
  10.             try{  
  11.                 wait();  
  12.             }catch(InterruptedException e){  
  13.                 e.printStackTrace();  
  14.             }             
  15.         }  
  16.         return (Request)queue.removeFirst();  
  17.     }  
  18.       
  19.     public synchronized void putRequest(Request r){  
  20.         queue.addLast(r);  
  21.         notifyAll();  
  22.     }     
  23. }  

* ClientThread 類 : 
ClientThread 類是用來模擬送出請求的線程. ClientThread 擁有 RequestQueue 的物件, 會不斷調用 putRequest. 
代碼 : 
  1. package dp.thread.ch3;  
  2.   
  3. import java.util.Random;  
  4.   
  5. public class ClientThread extends Thread{  
  6.     private Random random;  
  7.     private RequestQueue requestQueue;  
  8.     private int taskCount = 10000;  
  9.       
  10.     public ClientThread(RequestQueue rq, String name, long seed,int tc){  
  11.         super(name);  
  12.         this.requestQueue = rq;  
  13.         this.random = new Random(seed);  
  14.         if(tc>0){  
  15.             taskCount = tc;  
  16.         }  
  17.     }  
  18.       
  19.     public void run(){  
  20.         for(int i=0;i
  21.             Request request = new Request("No."+i);  
  22.             System.out.println(Thread.currentThread().getName()+" requests "+request);  
  23.             requestQueue.putRequest(request);  
  24.             try{  
  25.                 Thread.sleep(random.nextInt(1000));  
  26.             }catch(InterruptedException e){  
  27.                 e.printStackTrace();  
  28.             }  
  29.         }  
  30.     }  
  31. }  

* ServerThread 類 : 
ServerThread 類是用來接受請求的線程, 這個類也擁有 RequestQueue 的物件 (通常與 ClientThread 會是同一個) . ServerThread 會調用 getRequest 方法獲取請求. 
代碼 : 
  1. package dp.thread.ch3;  
  2.   
  3. import java.util.Random;  
  4.   
  5. public class ServerThread extends Thread{  
  6.     private Random random;  
  7.     private RequestQueue requestQueue;  
  8.     private int taskCount = 10000;  
  9.       
  10.     public ServerThread(RequestQueue rq, String name, long seed,int tc){  
  11.         super(name);  
  12.         this.requestQueue = rq;  
  13.         this.random = new Random(seed);  
  14.         if(tc>0){  
  15.             taskCount = tc;  
  16.         }  
  17.     }  
  18.       
  19.     public void run(){  
  20.         for(int i=0;i < taskCount;i++){  
  21.             Request request = requestQueue.getRequest();  
  22.             System.out.println(Thread.currentThread().getName()+" handles "+request);  
  23.             try{  
  24.                 Thread.sleep(random.nextInt(1000));  
  25.             }catch(InterruptedException e){  
  26.                 e.printStackTrace();  
  27.             }  
  28.         }  
  29.     }  
  30. }  

* Main 類 : 
在 Main 類中, 首先會先建立 RequestQueue 的實例, 並建立名為 Peter 的 ClientThread 與 名為 Mary 的 ServerThread, 並啟動它們. 
代碼 : 
  1. package dp.thread.ch3;  
  2.   
  3. public class Main {  
  4.   
  5.     public static void main(String[] args) {  
  6.         int taskCount = 50;  
  7.         RequestQueue requestQueue = new RequestQueue();       
  8.         new ClientThread(requestQueue,"Peter",31252L,taskCount).start();  
  9.         new ServerThread(requestQueue,"Mary",245191L,taskCount).start();  
  10.     }  
  11.   
  12. }  

執行結果 : 
Peter requests [ Request No.0 ]
Mary handles [ Request No.0 ]
Peter requests [ Request No.1 ]
Mary handles [ Request No.1 ]
Peter requests [ Request No.2 ]
Mary handles [ Request No.2 ]
Peter requests [ Request No.3 ]
Mary handles [ Request No.3 ]
Peter requests [ Request No.4 ]
Mary handles [ Request No.4 ]
Peter requests [ Request No.5 ]
...(以下省略)...

沒有留言:

張貼留言

網誌存檔

關於我自己

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