-
1 # 雲端的曼徹斯特
-
2 # 木法沙和三傻
1. ThreadLocal的值是存在它自己的內部類ThreadLocalMap的物件中的,ThreadLocalMap內部又定義了一個內部類Entity用來封裝ThreadLocalMap的k-v
2. Thread的一個成員變數是ThreadLocalMap型別
3 也就是說,ThreadLocal的存取值是依賴於當前執行緒的,值是存在當前執行緒的屬性中,無論ThreadLocal定義在哪,set和get都是要呼叫當前執行緒物件並在其中存取,都是開闢的單獨的空間,
4 不同執行緒下,操作的都是同一物件的副本,物件的屬性功能都在,卻相互獨立。ThreadLocal的物件自動跟當前執行緒物件建立聯絡。
5 比如DB連線中的con,就建立了一個,但多個執行緒都可以用,就是因為他們操作的是con的副本。
6 此時再回頭看 ThreadLocal的命名,就容易理解了,執行緒的區域性變數,從本質上說,透過ThreadLocalMap做中介,ThreadLocal物件透過set方法給當前Thread的一個成員變數賦值。ThreadLocal就相當於Thread的一個工具類,有2個作用:
(1) 定義ThreadLocalMap供Thread使用
(2) 為Thread的ThreadLocalMap屬性threadLocals提供維護介面。
-
3 # 程式猿W
ThreadLocal是什麼?
ThreadLocal是一個本地執行緒副本變數工具類。主要用於將私有執行緒和該執行緒存放的副本物件做一個對映,各個執行緒之間的變數互不干擾,在高併發場景下,可以實現無狀態的呼叫,特別適用於各個執行緒依賴不通的變數值完成操作的場景.
ThreadLocal特點:就是在一個執行緒裡放一個數據,不管中間執行了什麼操作。只要想獲取出來的時候,呼叫get就可以得到儲存進去的資料.
ThreadLocal內部結構圖從上面的結構圖中,我們可以看到ThreadLocal的核心機制
每個Thread 內部都有一個Map。Map裡面儲存執行緒本地物件(key) 和執行緒的變數副本(value)。Thread 內部的Map是由 ThreadLocal為的,由ThreadLocal負責向map獲取和設定執行緒的變數值。Thread執行緒內部的Map在類中描述如下:
ThreadLocal 為什麼會記憶體洩漏我們先分析一下ThreadLocalMap
我們可以知道每個Thread 維護一個 ThreadLocalMap,這個對映表的 key 是 ThreadLocal例項本身,value 是真正需要儲存的 Object,也就是說 ThreadLocal 本身並不儲存值,它只是作為一個 key 來讓執行緒從 ThreadLocalMap 獲取 value。仔細觀察ThreadLocalMap,這個map是使用 ThreadLocal 的弱引用作為 Key 的,弱引用的物件在 GC 時會被回收。
這樣,當把threadlocal變數置為null以後,沒有任何強引用指向threadlocal例項,所以threadlocal將會被gc回收。這樣一來,ThreadLocalMap中就會出現key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當前執行緒再遲遲不結束的話,這些key為null的Entry的value就會一直存在一條強引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,而這塊value永遠不會被訪問到了,所以存在著記憶體洩露。
其實java 開發者,也考慮到了此問題,所以在get(),set()的時候,呼叫了expungeStaleEntry方法用來清除Entry中Key為null的Value,但是這是不及時的,也不是每次都會執行的,所以一些情況下還是會發生記憶體洩露。只有remove()方法中顯式呼叫了expungeStaleEntry方法。
下面看下ThreadLocal 的get()方法的實現:
下面繼續看 map.getEntry方法
當key 為null 時,呼叫getEntryAfterMiss方法
當key 為null 時,呼叫expungeStaleEntry 方法
也許有人會好奇,有上面方法,為什麼還會導致記憶體洩漏呢?
一般我們設定的ThreadLocal設定為static的,static 變數可以作為GCRoot的根節點,所以會一直存在初始化了ThreadLocal, 呼叫set ,get 而沒有呼叫remove方法,所以會導致記憶體洩漏。比如get方法,只有ThreadLocalMap中沒有所需要的key時,才會呼叫清除方法
回覆列表
ThradLocal就是通過當前執行緒id作為建對映到一個共享變數副本到當前執行緒堆疊,這樣各個執行緒都有一個共享變數副本,就可以避免多個執行緒之間搶佔共享變數資源導致發生髒讀,幻度的情況,做到執行緒隔離。