1. 程式人生 > >執行緒本地變數 - ThreadLocal

執行緒本地變數 - ThreadLocal

參考網址: https://www.jianshu.com/p/98b68c97df9b (此處連結不知為何,點選後跳轉頁面還是本頁,建議複製開啟)

1、ThreadLocal是什麼?

執行緒本地變數,其為每個使用該變數的執行緒提供獨立的變數副本,所以每個執行緒都可以獨立的改變自己的副本,而不影響其它執行緒維護的副本。

在每個執行緒內部都有自己的一個Map,該MapThreadLocal中的一個內部類ThreadLocalMap。線上程對其進行讀寫時,是從本身擁有的成員變數threadlocals(型別為內部類ThreadLocalMap)中進行讀寫,該變數是用來儲存實際變數副本的。

儲存時鍵是當前ThreadLocal變數,值是要儲存的變數複本。

擴充套件:Thread中包含兩個ThreadLocal中的ThreadLocalMap成員變數,threadLocalsinheritableThreadLocals

2、ThreadLocal是如何為每個執行緒建立變數的副本的?

在每個執行緒Thread內部有一個ThreadLocal.ThreadLocalMap型別的成員變數threadLocals,鍵是當前ThreadLocal變數,值是變數的副本;

初始時,在Thread裡面,threadLocals為空,當通過ThreadLocal變數呼叫get()

set()時,就會對Thread類中的threadLocals進行初始化,並且以當前ThreadLocal變數為鍵,以ThreadLocal要儲存的副本變數為value

3ThreadLocalMap的解釋

ThreadLocal的內部類,類似於HashMap,但並沒有實現Map介面

初始容量是16Entry<K,V>陣列,這裡指定KThreadLocal物件,並且注意Entry<K,V>是繼承自WeakReference(弱引用,生命週期只能存活到下次GC前)

HashMap最大的不同是,Entry<K,V>沒有next,就是說,解決

Hash衝突的方式並不是連結串列的方式,而是採用線性探測法,簡單的步長加一或減一(尋找相鄰的位置)。該線性探測法解決hash衝突的效率低。

線性探測法:根據初始keyhashcode值確定元素的位置,若該位置已存在一個元素,則根據一定的演算法機制去下一處位置,直到存放元素成功。

4ThreadLocalget()set()方法解釋

get()

獲取當前執行緒的ThreadLocalMap物件ThreadLocals

map中獲取執行緒儲存的Entry<K,V>節點;

Entry<K,V>節點獲取儲存的Value副本值返回;

map為空的話返回初始值null,即執行緒變數副本為null,在使用時需要注意判斷NullPointerException

set()

獲取當前執行緒的成員變數map

map非空,則重新將ThreadLocal和新的value副本放入到map中;

map空,則對執行緒的成員變數ThreadLocalMap進行初始化建立,並將ThreadLocalvalue副本放入map中。

5ThreadLocal的問題,會造成記憶體洩露

ThreadLocalMap中的鍵是弱引用的,而value是強引用的,這樣會導致ThreadLocal在沒有外部物件強引用時,發生GC時弱引用key會被回收,而value不會被回收。此時建立ThreadLocal的執行緒還在執行,時間過長後,加之Entry物件的value一直得不到回收,發生記憶體洩露。

為此,在呼叫ThreadLocalget()set()後,需要呼叫remove()方法,將Entry節點和Map的引用關係溢位,這樣整個Entry物件在GC Roots分析後就變成不可達了,下次GC時就可以完成正確的回收。

6ThreadLocal的應用場景

只適用於獨立副本的情況,例如資料庫連線、Session管理等