hreadLocal,不少人都叫它作線程本地變量,也有些地方叫作線程本地存儲,其實意思差很少。java
可能不少朋友都知道ThreadLocal爲變量在每一個線程中都建立了一個副本,那樣每一個線程能夠訪問本身內部的副本變量。程序員
這句話從表面上看起來理解正確,但實際上這種理解是不太正確的。下面咱們細細道來。數據庫
多線程併發執行時,須要數據共享,所以纔有了volatile變量解決 多線程間的數據可見性,安全
也有了鎖的同步機制,使變量或代碼塊在某一時該,只能被一個線程訪問,確保共享數據的正確性。(Synchronized用於線程間的數據共享的)性能優化
多線程併發執行時,並非全部數據都須要共享的,這些不須要共享的數據,讓每一個線程去維護就OK了,ThreadLocal就是用於線程間的數據隔離的。多線程
給你們推薦一個程序員學習秋秋羣:945622618。羣裏有分享的視頻,還有思惟導圖
羣公告有視頻,都是乾貨的,你能夠下載來看。主要分享分佈式架構、高可擴展、高性能、高併發、性能優化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分佈式項目實戰學習架構師視頻。架構
深刻解析ThreadLocal類:併發
先咱們來看一下ThreadLocal類是如何爲每一個線程建立一個變量的副本的。分佈式
先看下get方法的實現:函數
第一句是取得當前線程,而後經過getMap(t)方法獲取到一個map,map的類型爲ThreadLocalMap。
而後接着下面獲取到Entry鍵值對,注意這裏獲取Entry時參數傳進去的是 this,即ThreadLocal實例,而不是當前線程t。若是獲取成功,則返回value值。
若是map爲空,則調用setInitialValue方法返回value。
接着看一下getMap方法中作了什麼:
在getMap中,是調用當期線程t,返回當前線程t中的一個成員變量threadLocals,類型爲ThreadLocalMap。
這裏意味着每個線程都自帶一個ThreadLocalMap成員變量。
繼續取看ThreadLocalMap的實現:
能夠看到ThreadLocalMap的Entry繼承了WeakReference,而且使用ThreadLocal做爲鍵值。
也就是說WeakReference封裝了ThreadLocal,並做爲了ThreadLocalMap的Entry的Key。
總結一下,在每一個線程Thread內部有一個ThreadLocalMap類型的成員變量threadLocals,
這個ThreadLocalMap成員變量的Entry的Key爲,當前ThreadLocal變量的WeakReference封裝,value爲變量。
爲什麼ThreadLocalMap的鍵值爲ThreadLocal對象? 由於每一個線程中可能須要有多個threadLocal變量,也就是ThreadLocalMap裏面可能會有多個Entry。
在每一個線程內部 第一次調用ThreadLocal.get方法時,都會返回Null。由於默認狀況下,initialValue方法返回的是null。
null 賦給(強轉) 基本數據類型時會拋的空指針,null賦給 引用類型沒問題。
能夠在ThreadLocal的構造函數重寫initialValue()方法。以下
ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){
protected Long initialValue() {
return Thread.currentThread().getId();
};
};
或者在調用ThreadLocal.get方法以前,須要先執行set(),以保證threadlocals中有值。
或者value爲引用類型變量,null賦給 引用類型沒問題。,以下,hibernate中典型的ThreadLocal的應用:
開篇說ThreadLocal建立副本 的說法是不太正確的。爲何?
從上面這個hibernate的例子來看,這是一個使用ThreadLocal解決數據庫鏈接的單例 在多線程中同時操做查詢和關閉的狀況。
首先這裏面不是建立副本,而是分發新的內存地址(即,新的數據庫鏈接的單例的內存地址),以當前ThreadLocal爲key,value指向傳入新的數據庫鏈接的單例的內存地址。
從而達到單個線程獲取數據鏈接的線程安全而已,也就是每一個線程都有一個獨立的數據庫鏈接的單例。
假設相反狀況,一個數據庫鏈接單例 若是在2個線程中被同時引用,2線程分別同一時間操做讀取和close,確定會出現衝突。
因此須要減小每次new的開銷仍是得使用數據庫鏈接池。
ThreadLocal的內存泄露問題:
當使用線程池來複用線程時,一個線程使用完後並不會銷燬線程,那麼 分發的那個實例會一直綁定在這個線程上。
因爲WeakReference封裝了ThreadLocal,並做爲了ThreadLocalMap的Entry的Key。若是在某些時候ThreadLocal對象被賦Null的話,弱引用會被GC收集,這樣就會致使Entry的Value對象找不到,
線程被複用後若是有調用ThreadLocal.get/set方法的話,方法裏面會去作遍歷清除 以[ThreadLocal=Null ]爲Key的Entry; 但若是一直沒調用ThreadLocal.get/set方法的話就會致使內存泄漏了。
因此通常線程用完ThreadLocal後,要調用threadLocal.remove(); 以下
給你們推薦一個程序員學習秋秋羣:945622618。羣裏有分享的視頻,還有思惟導圖
羣公告有視頻,都是乾貨的,你能夠下載來看。主要分享分佈式架構、高可擴展、高性能、高併發、性能優化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分佈式項目實戰學習架構師視頻。