2010年10月11日 星期一

[ Java設計模式 ] 多線程設計模式 : Thread-Specific Storage Pattern (每個線程的保管箱)

前言 : 

這裡是保管箱室, 在這裡有許多投幣式保管箱. 有個人拿著自己的Key 進來保管箱, 當他離開時也帶著保管箱的東西離開. 每個人雖然進到同一個保管箱室, 雖然面對許多的保管箱, 但是都能找到自己的保管箱並打開它. 這章要學習的是 Thread-Specific Storage Pattern. 它針對每個獨立線程提供特有的存儲空間. 雖然 Thread-Specific Storage Pattern 只有一個統一入口, 但內部會對每個線程提供特有的存儲空間. 使用Java 標準的 JDK時, 可以用 java.lang.ThreadLocal 類加以實現.


Thread-Specific Storage Pattern 所有參予者 : 
* Client (委託者) 參予者 : 
Client 參予者會將工作委託給 TSObjectProxy 參予者. 一個 TSObjectProxy 參予者可以由多個 Client 參予者一起使用. 範例程序中 Client 參予者是 ClientThread 類.

* TSObjectProxy (線程獨有對象的代理者) 參予者 : 
TSObejctProxy 參予者會處理多個Client 參予者委託的工作. 首先 TSObjectProxy參予者會使用 TSObjectCollection參予者取得Client 參予者所對應的 TSObject 參予者. 並將工作委託給該 TSObject 參予者. 範例程序中, TSObjectProxy 參予者是 Log 類.

* TSObjectCollection (線程獨有對象的集合) : 
TSObejctCollection 參予者擁有 Client 參予者與 TSObject 參予者的對照表. 當 getTSObject 方法被調用時, 就檢查對照表, 返回 Client 參予者對應的 TSObejct 參予者. 而 setTSObject 方法被調用時, 則在對照表設置 Client 參予者與 TSObject 參予者的組合. 範例程序中 TSObjectCollection 參予者是 java.lang.ThreadLocal 類.

* TSObject (線程獨有的對象) 參予者 : 
TSObject 參予者 存放線程特有的信息. TSObject 參予者的方法只會由單線程調用.範例程序中 TSObject 參予者是 TSLog 類.

* Thread-Specific Storage Pattern 類別圖 : 
 

範例程序 : 
接下來要寫的範例, 則是要將不同線程的輸出訊息寫到不同的log 文件裡. 
* TSLog 類 : 
在TSLog 類要對每個線程建立一個對應的物件. 提供個別 ClientThread 進行 log 動作, 代碼如下 : 
  1. package dp.thread.ch11;  
  2.   
  3. import java.io.*;  
  4.   
  5. public class TSLog {  
  6.     private PrintWriter writer = null;  
  7.   
  8.     public TSLog(String filename) {  
  9.         try{  
  10.             writer = new PrintWriter(new FileWriter(filename));  
  11.         }catch(IOException ioe) {  
  12.             ioe.printStackTrace();  
  13.         }  
  14.     }  
  15.   
  16.     public void println(String s) {  
  17.         writer.println(s);  
  18.     }  
  19.   
  20.     public void close(){  
  21.         writer.println("==== End of log ====");  
  22.         writer.close();  
  23.     }  
  24. }  
* Log 類 : 
他是提供 ClientThread 進行 log 動作的統一接口, 而之後的log 會 delegate 給 TSLog 進行操作, 代碼如下 : 
  1. package dp.thread.ch11;  
  2.   
  3. public class Log {  
  4.     private static final ThreadLocal tsLogCollection = new ThreadLocal();  
  5.   
  6.     public static void println(String s){  
  7.         getTSLog().println(s);  
  8.     }  
  9.   
  10.     public static void close(){  
  11.         getTSLog().close();  
  12.     }  
  13.   
  14.     private static TSLog getTSLog(){  
  15.         TSLog tsLog = (TSLog)tsLogCollection.get();  
  16.         if(tsLog == null) {  
  17.             tsLog = new TSLog(Thread.currentThread().getName()+"-log.txt");  
  18.             tsLogCollection.set(tsLog);  
  19.         }  
  20.         return tsLog;  
  21.     }  
  22. }  
* ClientThread 類 : 
ClientThread 是使用 Log 類進行log動作的線程. 代碼如下 : 
  1. package dp.thread.ch11;  
  2.   
  3. public class ClientThread extends Thread{  
  4.     public ClientThread(String name) {  
  5.         super(name);  
  6.     }  
  7.   
  8.     public void run(){  
  9.         System.out.println(getName() + " BEGIN");  
  10.         for(int i=0;i<10;i++) {  
  11.             Log.println("i="+i);  
  12.             try{  
  13.                 Thread.sleep(100);  
  14.             }catch(InterruptedException ioe) {  
  15.                 ioe.printStackTrace();  
  16.             }  
  17.         }  
  18.         Log.close();  
  19.         System.out.println(getName()+" END");  
  20.     }  
  21. }  
* Main 類 : 
為模擬使用 Thread-Specific Storage Pattern 的 Entry point. 會建立 3個ThreadClient 物件同時使用 Log類別進行 log動作. 代碼如下 : 
  1. package dp.thread.ch11;  
  2.   
  3. public class Main {  
  4.     public static void main(String args[]) {  
  5.         new ClientThread("John").start();  
  6.         new ClientThread("Peter").start();  
  7.         new ClientThread("Bob").start();  
  8.     }  
  9. }  


執行結果 : 
會依序建立 John-log.txt, Peter-log.txt 與 Bob-log.txt 檔案並記載如下內容 : 
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
==== End of log ====


補充說明 : 
@ 不必擔心被其他線程訪問 : 
Thread-Specific Pattern 是線程獨有的內存空間的意思. “線程獨有” 說起來文謅謅, 其實也就是 “線程自己的”. 再換個說法就是可以不必擔心被其他線程共享所會遇到的問題. 雖然說多線程程序設計中, 共享互斥很重要, 但要確實實現到共享互斥卻不容易. 而又共享互斥會使執行效能偏低, 所以要力求共享互斥的範圍盡可能縮小. 在 Thread-Specific Pattern 提供了以線程為鍵值來訪問線程獨有對象的機制. 這個對象會為每個線程分配且絕對不會被其他線程訪問. 因此 Thread-Specific Pattern 並沒有進行共享互斥的操作 (唯一可能是被隱藏在 TSObjectCollection 參予者 ).

@. 局部變量與 java.lang.ThreadLocal 類 : 
線程本來就有其特有的區域, 也就是存放方法局部變量的stack. 在方法裡分配的局部變量都是線程獨有, 無法由其他線程訪問. 但是這些變量一退出方法就會消失. 而 ThreadLocal 則是與方法調用無關, 為線程分配特有空間的類.

沒有留言:

張貼留言

[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...