Thread-Specific Storage就是「線程獨有的存儲庫」,該模式會對每一個線程提供獨有的內存空間。java.lang.ThreadLocal
類提供了該模式的實現,ThreadLocal的實例是一種集合(collection)架構,該實例管理了不少對象,能夠想象成一個保管有大量保險箱的房間。java
java.lang.ThreadLocal類的方法:安全
該方法會檢查當前調用線程,默認以該線程的Thread.currentThread()
值做爲鍵,來保存指定的值。數據結構
該方法會檢查當前調用線程,默認以該線程的Thread.currentThread()
值做爲鍵,獲取保存指定的值。架構
TSLog類:性能
//實際執行記錄日誌的類,每一個線程都會擁有一個該類的實例 public class TSLog { private PrintWriter writer = null; public TSLog(String filename) { try { writer = new PrintWriter(new FileWriter(filename)); } catch (IOException e) { e.printStackTrace(); } } public void println(String s) { writer.println(s); } public void close() { writer.println("==== End of log ===="); writer.close(); } }
Log類:this
public class Log { private static final ThreadLocal<TSLog> tsLogCollection = new ThreadLocal<TSLog>(); public static void println(String s) { getTSLog().println(s); } public static void close() { getTSLog().close(); } private static TSLog getTSLog() { TSLog tsLog = (TSLog) tsLogCollection.get(); if (tsLog == null) { tsLog = new TSLog(Thread.currentThread().getName() + "-log.txt"); tsLogCollection.set(tsLog); } return tsLog; } }
ClientThread類:spa
public class ClientThread extends Thread { public ClientThread(String name) { super(name); } public void run() { System.out.println(getName() + " BEGIN"); for (int i = 0; i < 10; i++) { Log.println("i = " + i); try { Thread.sleep(100); } catch (InterruptedException e) { } } Log.close(); System.out.println(getName() + " END"); } }
執行:
Alice、Bobby、Chris三個線程調用Log類的同一個方法,但實際上每一個線程都擁有獨自的TSLog實例。線程
public class Main { public static void main(String[] args) { new ClientThread("Alice").start(); new ClientThread("Bobby").start(); new ClientThread("Chris").start(); } }
Thread-Specific Storage模式的角色以下:設計
Client參與者會將工做委託給TSObjectProxy參與者。(案例中的ClientThread類就是Client)代理
TSObjectProxy參與者會處理多個Client委託的工做。(案例中的Log類就是TSObjectProxy)
(案例中的java.lang.ThreadLocal
類就是TSObjectCollection)
TSObject存放線程所特有的信息,TSObject實例的方法只會由單線程調用,由TSObjectCollection管理,每一個線程都擁有獨立的TSObject實例。(案例中的TSLog類就是TSObject)
ThreadLocal類主要有四個方法:
一、初始化返回值的方法:
該方法實現只返回 null,而且修飾符爲protected,很明顯,若是用戶想返回初始值不爲null,則須要重寫該方法;
protected T initialValue() { return null; }
二、get方法,獲取線程本地副本變量
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { T result = (T)e.value; return result; } } return setInitialValue(); }
三、set方法,設置線程本地副本變量
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
四、remove方法,移除線程本地副本變量
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
若是須要咱們本身來設計ThreadLocal對象,那麼,通常的實現思路:
設計一個線程安全的Map,key就是當前線程對象,Value就是線程本地變量的值。
然而,JDK的實現思路:
讓每一個Thread對象,自身持有一個Map,這個Map的Key就是當前ThreadLocal對象,Value是本地線程變量值。相對於加鎖的實現方式,這樣作能夠提高性能,實際上是一種以時間換空間的思路。
ThreadLocal類有個getMap()
方法,其實就是返回Thread對象自身的Map——threadLocals。
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
threadLocals是一種ThreadLocal.ThreadLocalMap
類型的數據結構,做爲內部類定義在ThreadLocal類中,其內部採用一種WeakReference的方式保存鍵值對。