2010年9月27日 星期一

[ Java設計模式 ] 多線程設計模式 : Balking Pattern (不需要的話, 就算了吧)

前言 : 

想像你正在餐廳裡, 考慮要吃啥東西. 因為決定要吃甚麼了便舉手向服務生示意點餐, 這時有兩位服務生都看到你舉手, 而B服務生看到A服務生已經往你走來, 所以就沒過來你這裡…
本章要學習的是 Balking Pattern. 當現在不適合進行這個操作, 或是沒有必要進行這個操作時就直接放棄進行這個操作而回去, 這就是Balking Pattern. Balk Pattern 與前一章一樣都需要警戒條件. 在 Balking Pattern 中當警戒條件不成立, 就馬上中斷, 而前一章則是會等待.
Balking Pattern 示意圖:


Pattern 參與者 : 
* GuardedObject (被守護的物件) : 
GuardedObject 是一個擁有被防衛的方法的類. 當線程執行 guardedMethod時, 只要滿足警戒條件便會馬上執行. 但若警戒條件不成立, 就不執行操作並直接退出. 警戒條件的成立與否會隨 GuardedObject 的狀態進行變化.


使用時機 : 
* 不需要刻意去等待而後執行 : 
像這樣 "不需要等待去執行" 的時候, 就可以使用 Balking Pattern. 在這裡 Balk 的原因是為了要提高程序執行的效能. 

* 不想等待警戒條件成立時 : 
Balking Pattern 的特徵是 "不等待" . 當警戒條件不成立, 就直接退出, 馬上進行下一個工作. 這是為了提高程序的響應性. 

* 警戒條件只有第一次成立 : 
當警戒條件只有第一次成立時, 也可以使用 Balking Pattern. 

使用範例 : 
接下來要來撰寫使用 Balking Pattern 的簡單程序. 在這裡要寫的程序是定期將現在的數據的屬性寫入到文件裡. 這裡會有一個屬性 changed 表示文件是否有改變過. 在進行保存時, 只有當有最新的數據時 (changed=true), 寫入的動作才會執行. 若無新數據時 (changed=false) 則就不做寫入的動作即 balk. 也就是說以 "數據是否為最新 (changed=true)" 作為警戒條件, 當不滿足則直接放棄寫入的動作.

* Data 類 : 
用來模擬數據實體, 具有方法 save 來進行保存的動作與 doSave 來進行寫入的動作. 代碼如下 : 
  1. package dp.thread.ch4;  
  2.   
  3. import java.io.*;  
  4.   
  5. public class Data {  
  6.     private final String filename;  //寫入檔案名  
  7.     private String content; //數據內容  
  8.     private boolean changed; //記錄是否數據是最新的  
  9.       
  10.     public Data(String fn,String content){  
  11.         this.filename = fn;  
  12.         this.content = content;  
  13.         this.changed = true;  
  14.     }  
  15.       
  16.     public synchronized void change(String newContent){   
  17.         content = newContent;  
  18.         changed = true;  
  19.     }  
  20.       
  21.     public synchronized boolean save() throws IOException{ //進行數據保存  
  22.         if(!changed){  
  23.             return false;  
  24.         }  
  25.         doSave();  
  26.         changed = false;  
  27.         return true;  
  28.     }  
  29.       
  30.     private void doSave()throws IOException{  //進行數據寫入  
  31.         System.out.println(Thread.currentThread().getName()+" calls doSave, content= "+content);  
  32.         Writer writer = new FileWriter(filename,true);  
  33.         writer.write(content+"\r\n");  
  34.         writer.close();  
  35.     }  
  36. }  
* SaverThread 類 : 
SaverThread 是用來定期要求保存數據的類別. 可透過建構子決定多久保存一次數據. 代碼如下 : 
  1. package dp.thread.ch4;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. public class SaverThread extends Thread{  
  6.     private Data data;  
  7.     private int tc = 100;  //Running times  
  8.     private long st = 1000;  //Thread sleep time (ms)  
  9.       
  10.     public SaverThread(String name, Data d, int c,long st){  
  11.         super(name);  
  12.         this.data = d;  
  13.         if(c>0){  
  14.             tc = c;  
  15.         }  
  16.         if(st>0){  
  17.             this.st = st;  
  18.         }  
  19.     }  
  20.       
  21.     public void run(){  
  22.         try{  
  23.             int count = 0;  
  24.             for(int i=0;i
  25.                 if(data.save()){  // 保存數據  
  26.                     count++;  
  27.                 }  
  28.                 Thread.sleep(st);  // 休息指定時間  
  29.             }  
  30.             System.out.println("End of SaverThread...("+count+")");  
  31.         }catch(IOException ioe){  
  32.             ioe.printStackTrace();  
  33.         }catch(InterruptedException e){  
  34.             e.printStackTrace();  
  35.         }  
  36.     }  
  37.   
  38. }  
* ChangerThread 類 : 
ChangerThread 類則是用來修改數據, 模擬使用者不訂時保存數據的動作, 代碼如下 : 
  1. package dp.thread.ch4;  
  2.   
  3. import java.io.*;  
  4. import java.util.Random;  
  5.   
  6. public class ChangerThread extends Thread{  
  7.     private Data data;  
  8.     private Random random = new Random();  
  9.     private int tc = 100;  //執行次數  
  10.       
  11.     public ChangerThread(String name,Data d, int c){  
  12.         super(name);  
  13.         this.data = d;        
  14.         if(c >0 ){  
  15.             tc = c;  
  16.         }  
  17.     }  
  18.       
  19.     public void run(){  
  20.         try{  
  21.             int count = 0;  
  22.             for(int i=0;i
  23.                 data.change("No."+i);  //修改數據  
  24.                 Thread.sleep(random.nextInt(1000));  //模擬去做別的事  
  25.                 if(data.save()){  //明確要求存檔.  
  26.                     count++;  
  27.                 }  
  28.             }         
  29.             System.out.println("End of ChangerThread...("+count+")");  
  30.         }catch( IOException ioe ){  
  31.             ioe.printStackTrace();  
  32.         }catch( InterruptedException e){  
  33.             e.printStackTrace();  
  34.         }  
  35.     }  
  36. }  
* Main 類別 : 
Main 類別則是用來建立以上物件並進行測試, 代碼如下 : 
  1. package dp.thread.ch4;  
  2.   
  3. public class Main {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         int taskCount = 10;  
  10.         Data data = new Data("data.txt","(empty)");  
  11.         new ChangerThread("ChangerThread",data,taskCount).start();  
  12.         new SaverThread("SaverThread",data,taskCount,1000).start();  
  13.   
  14.     }  
  15. }  
執行結果 : 
SaverThread calls doSave, content= No.0
ChangerThread calls doSave, content= No.1
SaverThread calls doSave, content= No.2
ChangerThread calls doSave, content= No.3
SaverThread calls doSave, content= No.4
ChangerThread calls doSave, content= No.5
SaverThread calls doSave, content= No.6
SaverThread calls doSave, content= No.7
ChangerThread calls doSave, content= No.8
SaverThread calls doSave, content= No.9
End of ChangerThread...(4)
End of SaverThread...(6)

Ps. 這裡會發現從No.0 ~ No.9 並不會重覆, 即 SaverThread 與 ChangerThread 並不會重複寫入數據. 

補充說明 : 
@. Balking Pattern 與 Guarded Suspension Pattern 的中間 : 
這一章所學的 Balking Pattern 中, 當警戒條件不成立, 會直接 Balk 退出. 而 Guarded Suspension Pattern, 當警戒條件不成立, 會等待到成立為止. 在 "balk 退出" 與 "等待到警戒條件成立為止" 兩種極端處理方法中, 還有一種折衷作法 - "在條件成立前, 等待一定時間". 等待一定時間, 看看警戒條件會不會成立, 如果還是不成立就 Balk 退出. 這種稱為guarded timed, 或是簡單的稱為 timeout.

@. wait 的結束是什麼時候 : 
若調用 Java 的 wait 方法加上參數, 可以指定 timeout 時間. 例如下面語句, 就是在物件obj 上調用 wait 方法, timeout 的時間約是 1000 ms.
obj.wait(1000); //指定 timeout 時間為 1000ms
執行這個語句時, 線程會進入 obj 的 wait set 暫停並釋放 obj 的鎖定. 只要發生以下狀況, 才會從等待區退出 :
* 當 notify 方法執行 : 對 obj 調用, 只有一條線程會被喚醒.
* 當 notifyAll 方法執行 : 對 obj 調用, 所有在 obj 的 wait set 等待的線程都會被喚醒.
* 當 interrupt 方法執行 : 對某條線程調用 interrupt 方法時, wait set 裡的線程重新獲取 obj 的鎖定, 並拋出 InterruptedException 異常.
* 當發生 timeout 時 : 當wait 方法中設置 timeout 時間且 timeout 已經到的時候, 該線程退出 wait set 並和 notify, notifyAll 一樣, 需要重新獲取 obj 的鎖定.

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...