標籤:first null sse author nbsp 都對 ble rgb class
一、ThreadLocal基礎知識
原始線程現狀: 按照傳統經驗,如果某個對象是非安全執行緒的,在多線程環境下,對對象的訪問必須採用synchronized進行線程同步。但是Spring中的各種模板類並未採用線程同步機制,因為線程同步會影響並發性和系統效能,而且實現難度也不小。
ThreadLocal在Spring中發揮著重要的作用。在管理request範圍的bean,交易管理,任務調度,AOP等模組中都出現了它的身影。
ThreadLocal介紹: 它不是一個線程,而是線程的一個本地化對象,或者說是局部變數。當工作於多線程中的對象使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的線程分配一個獨立的變數副本。所以每一個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的副本,將類變數放到ThreadLocal類型的對象中,使變數在每個線程中都有獨立拷貝,不會出現一個線程讀取變數時而被另一個線程修改的現象
如何存放執行緒區域變數?
在ThreadLocal類中有一個ThreadLocalMap, 用於存放每一個線程的變數副本,Map中元素的鍵為線程對象,值為對應線程的變數副本。二、源碼解讀
首先說明ThreadLocal存放的值是線程內共用的,線程間互斥的,主要用於線程內共用一些資料,避免通過參數來傳遞,這樣處理後,能夠優雅的解決一些實際問題,比如Hibernate中的OpenSessionInView,就是使用ThreadLocal儲存Session對象,還有我們經常用ThreadLocal存放Connection,代碼如:
1 1/** 2 * 資料庫連接管理類 3 * @author 爽 4 * 5 */ 6 6public class ConnectionManager { 7 8 /** 線程內共用Connection,ThreadLocal通常是全域的,支援泛型 */ 9 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); 10 11 public static Connection getCurrConnection() { 12 // 擷取當前線程內共用的Connection 13 Connection conn = threadLocal.get(); 14 try { 15 // 判斷串連是否可用 16 if(conn == null || conn.isClosed()) { 17 // 建立新的Connection賦值給conn(略) 18 // 儲存Connection 19 threadLocal.set(conn); 20 } 21 } catch (SQLException e) { 22 // 異常處理 23 } 24 return conn; 25 } 26 27 /** 28 * 關閉當前資料庫連接 29 */ 30 public static void close() { 31 // 擷取當前線程內共用的Connection 32 Connection conn = threadLocal.get(); 33 try { 34 // 判斷是否已經關閉 35 if(conn != null && !conn.isClosed()) { 36 // 關閉資源 37 conn.close(); 38 // 移除Connection 39 threadLocal.remove(); 40 conn = null; 41 } 42 } catch (SQLException e) { 43 // 異常處理 44 } 45 } 46 }
這樣處理的好處:
1 統一管理Connection;
2 不需要顯示傳參Connection,代碼更優雅;
3 降低耦合性。
ThreadLocal有四個方法,分別為:
initialValue
protected T initialValue()
返回此線程局部變數的當前線程的初始值。最多在每次訪問線程來獲得每個線程局部變數時調用此方法一次,即線程第一次使用 get() 方法訪問變數的時候。如果線程先於 get 方法調用 set(T) 方法,則不會線上程中再調用 initialValue 方法。
該實現只返回 null;如果程式員希望將線程局部變數初始化為 null 以外的某個值,則必須為 ThreadLocal建立子類,並重寫此方法。通常,將使用匿名內部類。initialValue 的典型實現將調用一個適當的構造方法,並返回新構造的對象。
返回:
返回此線程局部變數的初始值
get
public T get()
返回此線程局部變數的當前線程副本中的值。如果這是線程第一次調用該方法,則建立並初始化此副本。
返回:
此線程局部變數的當前線程的值
set
public void set(T value)
將此線程局部變數的當前線程副本中的值設定為指定值。許多應用程式不需要這項功能,它們只依賴於initialValue() 方法來設定線程局部變數的值。
參數:
value - 儲存在此線程局部變數的當前線程副本中的值。
remove
public void remove()
移除此線程局部變數的值。這可能有助於減少線程局部變數的儲存需求。如果再次訪問此線程局部變數,那麼在預設情況下它將擁有其 initialValue。
很多人對ThreadLocal存在一定的誤解,說ThreadLocal中有一個全域的Map,set時執行map.put(Thread.currentThread(), value),get和remove時也同理,但SUN的大師們是否是如此實現的,我們只能去看源碼了。
set方法:
1 /** 2 * Sets the current thread‘s copy of this thread-local variable 3 * to the specified value. Most subclasses will have no need to 4 * override this method, relying solely on the {@link #initialValue} 5 * method to set the values of thread-locals. 6 * 7 * @param value the value to be stored in the current thread‘s copy of 8 * this thread-local. 9 */ 10 public void set(T value) { 11 // 擷取當前線程對象 12 Thread t = Thread.currentThread(); 13 // 擷取當前執行緒區域變數Map 14 ThreadLocalMap map = getMap(t); 15 // map不為空白 16 if (map != null) 17 // 存值 18 map.set(this, value); 19 else 20 // 建立一個當前執行緒區域變數Map 21 createMap(t, value); 22 } 23 24 /** 25 * Get the map associated with a ThreadLocal. Overridden in 26 * InheritableThreadLocal. 27 * 28 * @param t the current thread 29 * @return the map 30 */ 31 ThreadLocalMap getMap(Thread t) { 32 // 擷取當前線程的本地變數Map 33 return t.threadLocals; 34 }
這裡注意,ThreadLocal中是有一個Map,但這個Map不是我們平時使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一個內部類,不對外使用的。當使用ThreadLocal存值時,首先是擷取到當前線程對象,然後擷取到當前執行緒區域變數Map,最後將當前使用的ThreadLocal和傳入的值放到Map中,也就是說ThreadLocalMap中存的值是[ThreadLocal對象, 存放的值],這樣做的好處是,每個線程都對應一個本地變數的Map,所以一個線程可以存在多個執行緒區域變數。
get方法:
1 /** 2 * Returns the value in the current thread‘s copy of this 3 * thread-local variable. If the variable has no value for the 4 * current thread, it is first initialized to the value returned 5 * by an invocation of the {@link #initialValue} method. 6 * 7 * @return the current thread‘s value of this thread-local 8 */ 9 public T get() { 10 Thread t = Thread.currentThread(); 11 ThreadLocalMap map = getMap(t); 12 if (map != null) { 13 ThreadLocalMap.Entry e = map.getEntry(this); 14 if (e != null) 15 return (T)e.value; 16 } 17 // 如果值為空白,則返回初始值 18 return setInitialValue(); 19 }
了之前set方法的分析,get方法也同理,需要說明的是,如果沒有進行過set操作,那從ThreadLocalMap中拿到的值就是null,這時get方法會返回初始值,也就是調用initialValue()方法,ThreadLocal中這個方法預設返回null。當我們有需要第一次get時就能得到一個值時,可以繼承ThreadLocal,並且覆蓋initialValue()方法
優秀博文:
ThreadLocal
內容來自:cnblogs:牛奶、不加糖
執行緒區域變數ThreadLocal源碼解讀