ThreadLocal簡析

簡介

  ThreadLocal在Java多線程開發中常見的一個類,在面試中也經見的問題,好比ThreadLocal的做用是什麼,ThreadLocal的實現原理是什麼等等。ThreadLocal是java中一個類,用於實現變量在多線程併發環境下維持線程的封閉性(封閉指的是可變對象對於其餘線程是不可訪問,不可操做的)。因此ThreadLocal其實能夠當作一個線程內的局部變量來理解。java

 

使用場景

當咱們建立一個數據庫鏈接,而且不但願在代碼中互相傳遞,咱們會將它設置成全局變量。然而數據庫鏈接並不必定是線程安全的,咱們但願每個線程維持一個鏈接。面試

1 private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
2   public Connection initialValue() {
3     return DriverManager.getConnection(DB_URL);
4   }
5 };
6  
7 public static Connection getConnection() {
8   return connectionHolder.get();
9 }

  如上述代碼,當不一樣的線程來調用getConnection方法的時候,獲取到是不同的Connection對象。hibernate的session管理也是經過ThreadLocal來實現的。數據庫

 

源碼分析

  先來看一下ThreadLocal中最經常使用的幾個方法。安全

 1     public T get() {
 2         Thread t = Thread.currentThread();
 3         ThreadLocalMap map = getMap(t);
 4         if (map != null) {
 5             ThreadLocalMap.Entry e = map.getEntry(this);
 6             if (e != null) {
 7                 @SuppressWarnings("unchecked")
 8                 T result = (T)e.value;
 9                 return result;
10             }
11         }
12         return setInitialValue();
13     }
14 
15     ThreadLocalMap getMap(Thread t) {
16         return t.threadLocals;
17     }
18     
19     public void set(T value) {
20         Thread t = Thread.currentThread();
21         ThreadLocalMap map = getMap(t);
22         if (map != null)
23             map.set(this, value);
24         else
25             createMap(t, value);
26     }    

 

  ThreadLocal的get方法和set方法算是最經常使用的方法了。第3行和第21中對ThreadLocal的操做都是經過一個ThreadLocalMap進行讀寫來完成get和set,ThreadLocalMap是一個散列表。從第5行和第23行看出,ThreadLocalMap的key是this,指代的是ThreadLocal自己,value則是線程對應的變量值。有一點要注意的是,ThreadlocalMap是在ThreadLocal內部定義的靜態內部類,倒是Thread類的屬性。session

  剛纔說過ThreadlocalMap是一個散列表,可是它跟HashMap不同。HashMap解決Hash衝突的方式是使用分離鏈表法,可是ThreadlocalMap是經過線性探測法來解決Hash衝突,這裏就不對這方面進行細講。多線程

  

內存泄漏

  簡介了ThreadLocal不順便講一講ThreadLocal內存泄漏問題感受很不專業,可是講這個問題以前,能夠先看一下上面圖中,ThreadLocalMap的Entry使用的是一個弱引用(WeakReference),Java的引用分爲強軟弱虛四種,弱引用的特色是,當一個對象只被一個弱引用連接的時候,下次GC就會將剛對象回收。其餘的引用細節這裏不作贅述。併發

  內存泄漏緣由:當一個ThreadLocal對象使用完畢被系統回收以後,由於Entry中的key爲弱引用(這裏並不是整個entry),ThreadLocalMap中就有一個Entry中的Key會被設置爲Null,此時value仍然保存和entry之間的強連接,致使value沒法回收。假如線程一直不關閉,而ThreadLocalMap有大量的key被回收,就會存在一堆entry的key爲null,value各不相同且沒法回收的情況,因此ThreadLocal的get、set、remove中都會進行對key==null的entry進行清理。源碼分析

   有人說內存泄漏是由於弱引用引發的,其實並非,若是使用強引用,則會致使必須手動清除不須要使用的entry。使用上更加麻煩,而且更加容易出Bug。this

  總的來講,當ThreadLocal變量已經不使用的時候,最好作一次remove()動做進行清理,這會是一個好習慣。spa

相關文章
相關標籤/搜索