ThreadLocal-分析-總結

ThreadLocal<T>類在Spring,Hibernate等框架中起到了很大的作用,對於其工作原理,很多網上的文章分析的不夠徹底,甚至有些誤解。

 

首先,爲了解釋ThreadLocal類的工作原理,必須同時介紹與其工作甚密的其他幾個類(內部類)

1.ThreadLocalMap

2.Thread

可能有人會覺得Thread與ThreadLocal有什麼關係,其實真正的奧祕就在Thread類中的一行:

 

Java代碼  收藏代碼
  1. ThreadLocal.ThreadLocalMap threadLocals = null;  

 

 其中ThreadLocalMap的定義是在ThreadLocal類中,真正的引用卻是在Thread類中

 

那麼ThreadLocalMap究竟是什麼呢?

 

可以看到這個類應該是一個Map,JDK的解釋是

 

 寫道
ThreadLocalMap is a customized hash map suitable only for maintaining thread local values
 

 

接下來的重點是ThreadLocalMap中用於存儲數據的entry

 

Java代碼  收藏代碼
  1. static class Entry extends WeakReference<ThreadLocal> {  
  2.             /** The value associated with this ThreadLocal. */  
  3.             Object value;  
  4.   
  5.             Entry(ThreadLocal k, Object v) {  
  6.                 super(k);  
  7.                 value = v;  
  8.             }  
  9.         }  

 

 從中我們可以發現這個Map的key是ThreadLocal變量,value爲用戶的值,並不是網上大多數的列子key是線程的名字或者標識

 

到這裏,我們就可以理解ThreadLocal究竟是如何工作的了

 

1.Thread類中有一個成員變量叫做ThreadLocalMap,它是一個Map,他的Key是ThreadLocal類

2.每個線程擁有自己的申明爲ThreadLocal類型的變量,所以這個類的名字叫'ThreadLocal':線程自己的(變量)

3.此變量生命週期是由該線程決定的,開始於第一次初始(get或者set方法)

4.由ThreadLocal的工作原理決定了:每個線程獨自擁有一個變量,並非共享或者拷貝

 

Java代碼  收藏代碼
  1. /** 
  2.  * @author mxdba 
  3.  * 
  4.  */  
  5. public class ThreadLocalSample {  
  6.   
  7.     public static void main(String[] args) {  
  8.         ThreadTest test1 = new ThreadTest(10);  
  9.         ThreadTest test2 = new ThreadTest(20);  
  10.         test1.start();  
  11.         test2.start();  
  12.     }  
  13.   
  14. }  
  15.   
  16. /** 
  17.  * 此線程有兩個ThreadLocal變量,但是由於ThreadLocal是延遲初始的, 
  18.  * 所以在debug時可以看到線程名爲“線程20”的線程的ThreadLocalMap中沒有thLcal2這個entry 
  19.  * @author mxdba 
  20.  *  
  21.  */  
  22. class ThreadTest extends Thread {  
  23.       
  24.     public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();  
  25.     public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>();  
  26.       
  27.     public Integer num;  
  28.       
  29.       
  30.       
  31.     public ThreadTest(Integer num) {  
  32.         super("線程" + num);  
  33.         this.num = num;  
  34.     }  
  35.   
  36.     @Override  
  37.     public void run() {  
  38.         Integer n = thLocal.get();  
  39.         if(num != 20) {  
  40.             String s = thLocal2.get();  
  41.         }  
  42.               
  43.         if(n == null) {  
  44.             thLocal.set(num);  
  45.         }  
  46.         System.out.println(thLocal.get());  
  47.     }  
  48.       
  49. }  

 

 

接下來分析一下源碼,就更加清楚了

 

關鍵方法代碼  收藏代碼
  1. /**  
  2.  * 關鍵方法,返回當前Thread的ThreadLocalMap  
  3.  * [[[每個Thread返回各自的ThreadLocalMap,所以各個線程中的ThreadLocal均爲獨立的]]]  
  4.  */  
  5. ThreadLocalMap getMap(Thread t) {  
  6.         return t.threadLocals;  
  7.     }  
 

 

 

 

Threadlocal的get方法代碼  收藏代碼
  1. public T get() {  
  2.         Thread t = Thread.currentThread();  
  3.         /**  
  4.          * 得到當前線程的ThreadLocalMap  
  5.          */  
  6.         ThreadLocalMap map = getMap(t);  
  7.         if (map != null) {  
  8.             /**  
  9.              * 在此線程的ThreadLocalMap中查找key爲當前ThreadLocal對象的entry  
  10.              */  
  11.             ThreadLocalMap.Entry e = map.getEntry(this);  
  12.             if (e != null)  
  13.                 return (T)e.value;  
  14.         }  
  15.         return setInitialValue();  
  16.     }  
 

 

 

 

初始化方法代碼  收藏代碼
  1. private T setInitialValue() {  
  2.         /**  
  3.          * 默認返回null,這個方法爲protected可以繼承  
  4.          */  
  5.         T value = initialValue();  
  6.         Thread t = Thread.currentThread();  
  7.         ThreadLocalMap map = getMap(t);  
  8.         if (map != null)  
  9.             map.set(this, value);  
  10.         else  
  11.             /**  
  12.              * 初次創建  
  13.              */  
  14.             createMap(t, value);  
  15.         return value;  
  16.     }  
 

 

 

Java代碼  收藏代碼
  1. /** 
  2.  * 給當前thread初始ThreadlocalMap 
  3.  */  
  4. void createMap(Thread t, T firstValue) {  
  5.         t.threadLocals = new ThreadLocalMap(this, firstValue);  
  6.     }  
 

 

 

通過上邊的分析,我們發現,ThreadLocal類的使用雖然是用來解決多線程的問題的,但是還是有很明顯的針對性

1.最明顯的,ThreadLoacl變量的活動範圍爲某線程,並且我的理解是該線程“專有的,獨自霸佔”,對該變量的所有操作均有該線程完成!也就是說,ThreadLocal不是用來解決共享,競爭問題的。典型的應用莫過於Spring,Hibernate等框架中對於多線程的處理了

 

Java代碼  收藏代碼
  1. private static final ThreadLocal threadSession = new ThreadLocal();    
  2.     
  3. public static Session getSession() throws InfrastructureException {    
  4.     Session s = (Session) threadSession.get();    
  5.     try {    
  6.         if (s == null) {    
  7.             s = getSessionFactory().openSession();    
  8.             threadSession.set(s);    
  9.         }    
  10.     } catch (HibernateException ex) {    
  11.         throw new InfrastructureException(ex);    
  12.     }    
  13.     return s;    
  14. }    

 這段代碼,每個線程有自己的ThreadLocalMap,每個ThreadLocalMap中根據需要初始加載threadSession,這樣的好處就是介於singleton與prototype之間,應用singleton無法解決線程,應用prototype開銷又太大,有了ThreadLocal之後就好了,對於需要線程“霸佔”的變量用ThreadLocal,而該類實例的方法均可以共享。

 

2.關於內存泄漏:

雖然ThreadLocalMap已經使用了weakReference,但是還是建議能夠顯示的使用remove方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章