Java執行緒(篇外篇):執行緒本地變數ThreadLocal

OkidoGreen發表於2017-03-27

     首先說明ThreadLocal存放的值是執行緒內共享的,執行緒間互斥的,主要用於執行緒內共享一些資料,避免通過引數來傳遞,這樣處理後,能夠優雅的解決一些實際問題,比如hibernate中的OpenSessionInView,就是使用ThreadLocal儲存Session物件,還有我們經常用ThreadLocal存放Connection,程式碼如:

[java] view plain copy
 print?
  1. /** 
  2.  * 資料庫連線管理類 
  3.  * @author 爽 
  4.  * 
  5.  */  
  6. public 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方法:

[java] view plain copy
 print?
  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方法:

[java] view plain copy
 print?
  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()方法。

       (完)

       本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/15732053

相關文章