ThreadLocal內部機制及使用方法


一、介紹ThreadLocal內部機制之前,先簡單說明一下其特點及用途:

1.ThreadLocal是單線程內共享資源,多線程間無法共享(即線程A訪問不了線程B中ThreadLocal存放的值);

2.ThreadLocal是本地變量,無法跨jvm傳遞;

3.ThreadLocal的出現可以減少通過參數來傳遞(使代碼更加簡潔,降低耦合性),Hibernate中的OpenSessionInView,就始終保證當前線程只有一個在使用中的Connection(或Hibernate Session),代碼如下:

 1 public class ConnectionManager {  
 2   
 3     /** 線程內共享Connection,ThreadLocal通常是全局的,支持泛型 */  
 4     private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();  
 5       
 6     public static Connection getCurrConnection() {  
 7         // 獲取當前線程內共享的Connection  
 8         Connection conn = threadLocal.get();  
 9         try {  
10             // 判斷連接是否可用  
11             if(conn == null || conn.isClosed()) {  
12                 // 創建新的Connection賦值給conn(略)  
13                 // 保存Connection  
14                 threadLocal.set(conn);  
15             }  
16         } catch (SQLException e) {  
17             // 異常處理  
18         }  
19         return conn;  
20     }  
21       
22     /** 
23      * 關閉當前數據庫連接 
24      */  
25     public static void close() {  
26         // 獲取當前線程內共享的Connection  
27         Connection conn = threadLocal.get();  
28         try {  
29             // 判斷是否已經關閉  
30             if(conn != null && !conn.isClosed()) {  
31                 // 關閉資源  
32                 conn.close();  
33                 // 移除Connection  
34                 threadLocal.remove();  
35                 conn = null;  
36             }  
37         } catch (SQLException e) {  
38             // 異常處理  
39         }  
40     }  
41 }  


二、ThreadLocal的內部方法

1.protected T initialValue()

①該方法實現只返回 null,並且修飾符為protected,很明顯,如果用戶想返回初始值不為null,則需要定義線程變量時重寫該方法;

1     /**
2      * @return the initial value for this thread-local
3      */
4     protected T initialValue() {
5         return null;
6     }
1 //定義ThreadLocal時重寫initialValue方法,返回用戶想要的值
2 private static ThreadLocal t = new ThreadLocal() {
3     public Object initialValue() {
4         A a = new A();
5         return a;
6     }
7 };

②返回此線程局部變量的當前線程的初始值。最多在每次訪問線程來獲得每個線程局部變量時調用此方法一次,即線程第一次使用 get() 方法訪問變量的時候。如果線程先於 get 方法調用 set(T) 方法,則不會在線程中再調用 initialValue 方法。

 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         return setInitialValue();
18     }    

2.public T get()
①在1中已經提到,該方法返回當前線程變量副本。如果這是線程第一次調用該方法,則創建並初始化此副本。

3.public void set(T value)

①ThreadLocal中有個內部靜態類ThreadLocalMap,用來存放當前線程變量副本中的值,鍵為當前線程變量對象,值為用戶設的值;

②當使用ThreadLocal存值時,首先是獲取到當前線程對象,然后獲取到當前線程本地變量Map,最后將當前使用的ThreadLocal和傳入的值放到Map中,也就是說ThreadLocalMap中存的值是[ThreadLocal對象, 存放的值],這樣做的好處是,每個線程都對應一個本地變量的Map,所以一個線程可以存在多個線程本地變量(即不同的ThreadLocal,就如1中所說,可以重寫initialValue,返回不同類型的子類)。

 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         Thread t = Thread.currentThread();
12         ThreadLocalMap map = getMap(t);
13         if (map != null)
14             map.set(this, value);
15         else
16             createMap(t, value);
17     }

4.public void remove()

①移除此線程中某個ThreadLocal的值,目的是為了減少內存的占用。如果再次訪問此線程局部變量,那么在默認情況下它將擁有其initialValue;

②只有從jdk1.5開始才有該方法;

③當線程結束后,對應該線程的局部變量將自動被垃圾回收,因此顯式調用remove清除線程的局部變量並不是必須的操作,但它可以加快內存回收的速度。

 1      /**
 2      * Removes the current thread's value for this thread-local
 3      * variable.  If this thread-local variable is subsequently
 4      * {@linkplain #get read} by the current thread, its value will be
 5      * reinitialized by invoking its {@link #initialValue} method,
 6      * unless its value is {@linkplain #set set} by the current thread
 7      * in the interim.  This may result in multiple invocations of the
 8      * <tt>initialValue</tt> method in the current thread.
 9      *
10      * @since 1.5
11      */
12      public void remove() {
13          ThreadLocalMap m = getMap(Thread.currentThread());
14          if (m != null)
15              m.remove(this);
16      }

三、ThreadLocal和同步機制synchonzied相比

1.synchonzied同步機制是為了實現同步多線程對相同資源的並發訪問控制。同步的主要目的是保證多線程間的數據共享。同步會帶來巨大的性能開銷,所以同步操作應該是細粒度的(對象中的不同元素使用不同的鎖,而不是整個對象一個鎖)。如果同步使用得當,帶來的性能開銷是微不足道的。使用同步真正的風險是復雜性和可能破壞資源安全,而不是性能。 

2.ThreadLocal以空間換取時間,提供了一種非常簡便的多線程實現方式。因為多個線程並發訪問無需進行等待,所以使用ThreadLocal會獲得更大的性能。

3.ThreadLocal中的對象,通常都是比較小的對象。另外使用ThreadLocal不能使用原子類型,只能使用Object類型。ThreadLocal的使用比synchronized要簡單得多。 

4.synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的並不是同一個對象,這樣就隔離了多個線程對數據的數據共享。而Synchronized卻正好相反,它用於在多個線程間通信時能夠獲得數據共享。 

5.Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM