| | |資料來源:|[結城浩 - The Essence of Programming|http://www.hyuki.com/dp/dpinfo_ThreadSpecificStorage.html]| |
| | |作者:|結城浩| |
| | |翻譯:|koji| |
| | |
| | |
| | |
| | Thread-Specific Storage pattern |
| | 假設有一個如下的interface,當我呼叫完call以後,想知道是否有發生error, |
| | 或者發生什麼error,那麼就用errno method來調查.(這個前提聽起來有一點笨, |
| | 但是在實際的系統中卻常存在) |
| | |
| | |
| | {code:title=LegacySystem.java|borderStyle=solid} |
| | interface LegacySystem { |
| | public void call(int parameter); |
| | public int errno(); |
| | } |
| | {code} |
| | |
| | 在這邊我們實做一下上面的interface, |
| | 為了簡單一點,在這邊不做任何複雜動作,只單純把parameter |
| | 傳給errno |
| | |
| | {code:title=LegacySystemImpl.java|borderStyle=solid} |
| | class LegacySystemImpl implements LegacySystem { |
| | private int errno; |
| | public void call(int parameter) { errno = parameter; } |
| | public int errno() { return errno; } |
| | } |
| | {code} |
| | 上面的code很簡單只是傳入參數 |
| | 還有印出的動作 |
| | |
| | 如果在single thread下,上面的class不會有任何問題. |
| | 但是當LegacySystemImpl物件被複數的thread存取, |
| | 那麼將會發生問題.也就是說其他的thread呼叫call會影響到自己呼叫errno method的結果. |
| | |
| | 主要的如下 |
| | 現在兩個thread去呼叫 |
| | 第一個thread一直去呼叫call(0), |
| | 然後5秒以後第二個thread一直呼叫call(1) |
| | 如果發生傳入call的值和errno method回傳的值不同時, |
| | 印出???並呼叫System.exit結束 |
| | |
| | {code:title=Main1.java|borderStyle=solid} |
| | class Main1 extends Thread { |
| | private static LegacySystem system = new LegacySystemImpl(); |
| | private int value = 0; |
| | public Main1(int value) { |
| | this.value = value; |
| | } |
| | public void run() { |
| | while (true) { |
| | System.out.println(Thread.currentThread().getName() + " checks."); |
| | system.call(value); |
| | try { Thread.sleep(100); } catch (InterruptedException e) {} |
| | int errno = system.errno(); |
| | System.out.println(Thread.currentThread().getName() + ": value = " + value + ", errno = " + errno); |
| | if (value != errno) { |
| | System.out.println("???"); |
| | System.exit(0); |
| | } |
| | } |
| | } |
| | public static void main(String[] args) { |
| | new Main1(0).start(); |
| | try { Thread.sleep(5000); } catch (InterruptedException e) {} |
| | new Main1(1).start(); |
| | } |
| | } |
| | {code} |
| | |
| | 結果如下 |
| | Thread-0 checks. |
| | Thread-0: value = 0, errno = 0 |
| | Thread-0 checks. |
| | Thread-0: value = 0, errno = 0 |
| | Thread-0 checks. |
| | Thread-0: value = 0, errno = 0 |
| | (中略) |
| | Thread-0 checks. |
| | Thread-0: value = 0, errno = 0 |
| | Thread-0 checks. |
| | Thread-1 checks. ←Thread-1啟動以後... |
| | Thread-0: value = 0, errno = 1 ←結果亂七八糟囉。 |
| | ??? |
| | |
| | 所以現在改寫成如下的proxy |
| | |
| | |
| | {code:title=LegacySystemProxy.java|borderStyle=solid} |
| | class LegacySystemProxy implements LegacySystem { |
| | private ThreadLocal thlocal = new ThreadLocal(); |
| | public void call(int parameter) { |
| | getImpl().call(parameter); |
| | } |
| | public int errno() { |
| | return getImpl().errno(); |
| | } |
| | private LegacySystemImpl getImpl() { |
| | LegacySystemImpl impl = (LegacySystemImpl)thlocal.get(); |
| | if (impl == null) { |
| | impl = new LegacySystemImpl(); |
| | thlocal.set(impl); |
| | } |
| | return impl; |
| | } |
| | }{code} |
| |  | 在這邊使用的java.lang.ThreadLocal,讓現在的thread保有其固有的領域(對thread來說specific的領域),為了讓thread有固有的LegacySystemImpl,所以使用threadlocal |
| | | 在這邊使用的java.lang.ThreadLocal,讓現在的thread保有其固有的領域(對thread來說specific的領域),為了讓thread有固有的LegacySystemImpl,所以使用threadlocal. |
| | |
| | 使用threadlocal的get和set method可以去讀寫現在的thread的固有領域 |
| | |
| | 寫個新的main |
 | | |
| | |
| | {code:title=Main2.java|borderStyle=solid} |
| | class Main2 extends Thread { |
| | private static LegacySystem system = new LegacySystemProxy(); |
| | private int value = 0; |
| | public Main2(int value) { |
| | this.value = value; |
| | } |
| | public void run() { |
| | while (true) { |
| | System.out.println(Thread.currentThread().getName() + " checks."); |
| | system.call(value); |
| | try { Thread.sleep(100); } catch (InterruptedException e) {} |
| | int errno = system.errno(); |
| | System.out.println(Thread.currentThread().getName() + ": value = " + value + ", errno = " + errno); |
| | if (value != errno) { |
| | System.out.println("???"); |
| | System.exit(0); |
| | } |
| | } |
| | } |
| | public static void main(String[] args) { |
| | new Main2(0).start(); |
| | try { Thread.sleep(5000); } catch (InterruptedException e) {} |
| | new Main2(1).start(); |
| | } |
| | } |
| | {code} |
| | |
| | 結果如下 |
| | |
| | Thread-0 checks. |
| | Thread-0: value = 0, errno = 0 |
| | (中略) |
| | Thread-0 checks. |
| | Thread-1 checks. |
| | Thread-0: value = 0, errno = 0 |
| | Thread-0 checks. |
| | Thread-1: value = 1, errno = 1 |
| | Thread-1 checks. |
| | Thread-0: value = 0, errno = 0 |
| | Thread-0 checks. ←就像這樣 |
| | Thread-1 checks. ←就算這樣也不怕。 |
| | Thread-0: value = 0, errno = 0 |
| | Thread-0 checks. |
| | Thread-1: value = 1, errno = 1 |
| | Thread-1 checks. |
| | Thread-0: value = 0, errno = 0 |
| | (後略) |
| | |
| | 以上pattern在以下書籍的p475中介紹,解說文章及程式則由結城先生新撰寫的. |
| | |
| | \[POSA2\] Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects |
| | |
| | http://www.amazon.co.jp/exec/obidos/ASIN/0471606952/249-1714634-2037127 |
| | |
| | 此pattern在以下書籍也有 |
| | http://www.hyuki.com/dp/dp2.html (日文) |