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