ThreadLocal 是一個本地線程副本變量工具類。主要用於將私有線程和該線程存放的副本對象作一個映射,各個線程之間的變量互不干擾,在高併發場景下,能夠實現無狀態的調用,適用於各個線程不共享變量值的操做。算法
ThreadLocal 原理:每一個線程的內部都維護了一個 ThreadLocalMap,它是一個 Map(key,value)數據格式,key 是一個弱引用,也就是 ThreadLocal 自己,而 value 存的是線程變量的值。數組
也就是說 ThreadLocal 自己並不存儲線程的變量值,它只是一個工具,用來維護線程內部的 Map,幫助存和取變量。網絡
數據結構,以下圖所示:session
(圖片來源於網絡)數據結構
與 HashMap 不一樣,ThreadLocalMap 結構很是簡單,沒有 next 引用,也就是說 ThreadLocalMap 中解決 Hash 衝突的方式並不是鏈表的方式,而是採用線性探測的方式。所謂線性探測,就是根據初始 key 的 hashcode 值肯定元素在 table 數組中的位置,若是發現這個位置上已經被其餘的 key 值佔用,則利用固定的算法尋找必定步長的下個位置,依次判斷,直至找到可以存放的位置。併發
源代碼實現以下:ide
/
高併發
* Increment i modulo len.
工具
*/
spa
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
ThreadLocal 在 ThreadLocalMap 中是以一個弱引用身份被 Entry 中的 Key 引用的,所以若是 ThreadLocal 沒有外部強引用來引用它,那麼 ThreadLocal 會在下次 JVM 垃圾收集時被回收。這個時候 Entry 中的 key 已經被回收,可是 value 又是一強引用不會被垃圾收集器回收,這樣 ThreadLocal 的線程若是一直持續運行,value 就一直得不到回收,這樣就會發生內存泄露。
咱們知道 ThreadLocalMap 中的 key 是弱引用,而 value 是強引用纔會致使內存泄露的問題,至於爲何要這樣設計,這樣分爲兩種狀況來討論:
key 使用強引用:這樣會致使一個問題,引用的 ThreadLocal 的對象被回收了,可是 ThreadLocalMap 還持有 ThreadLocal 的強引用,若是沒有手動刪除,ThreadLocal 不會被回收,則會致使內存泄漏。
key 使用弱引用:這樣的話,引用的 ThreadLocal 的對象被回收了,因爲 ThreadLocalMap 持有 ThreadLocal 的弱引用,即便沒有手動刪除,ThreadLocal 也會被回收。value 在下一次 ThreadLocalMap 調用 set、get、remove 的時候會被清除。
比較以上兩種狀況,咱們能夠發現:因爲 ThreadLocalMap 的生命週期跟 Thread 同樣長,若是都沒有手動刪除對應 key,都會致使內存泄漏,可是使用弱引用能夠多一層保障,弱引用 ThreadLocal 不會內存泄漏,對應的 value 在下一次 ThreadLocalMap 調用 set、get、remove 的時候被清除,算是最優的解決方案。
ThreadLocal 適用於獨立變量副本的狀況,好比 Hibernate 的 session 獲取場景。
示例代碼:
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
public static Session getCurrentSession(){
Session session = threadLocal.get();
try {
if(session ==null&&!session.isOpen()){
//...
}
threadLocal.set(session);
} catch (Exception e) {
// TODO: handle exception
}
return session;
}