執行緒本地變數 - ThreadLocal
參考網址: https://www.jianshu.com/p/98b68c97df9b (此處連結不知為何,點選後跳轉頁面還是本頁,建議複製開啟)
1、ThreadLocal是什麼?
執行緒本地變數,其為每個使用該變數的執行緒提供獨立的變數副本,所以每個執行緒都可以獨立的改變自己的副本,而不影響其它執行緒維護的副本。
在每個執行緒內部都有自己的一個Map,該Map是ThreadLocal中的一個內部類ThreadLocalMap。線上程對其進行讀寫時,是從本身擁有的成員變數threadlocals(型別為內部類ThreadLocalMap)中進行讀寫,該變數是用來儲存實際變數副本的。
擴充套件:Thread中包含兩個ThreadLocal中的ThreadLocalMap成員變數,threadLocals、inheritableThreadLocals
2、ThreadLocal是如何為每個執行緒建立變數的副本的?
在每個執行緒Thread內部有一個ThreadLocal.ThreadLocalMap型別的成員變數threadLocals,鍵是當前ThreadLocal變數,值是變數的副本;
初始時,在Thread裡面,threadLocals為空,當通過ThreadLocal變數呼叫get()
3、ThreadLocalMap的解釋
是ThreadLocal的內部類,類似於HashMap,但並沒有實現Map介面
初始容量是16的Entry<K,V>陣列,這裡指定K為ThreadLocal物件,並且注意Entry<K,V>是繼承自WeakReference(弱引用,生命週期只能存活到下次GC前)
與HashMap最大的不同是,Entry<K,V>沒有next,就是說,解決
線性探測法:根據初始key的hashcode值確定元素的位置,若該位置已存在一個元素,則根據一定的演算法機制去下一處位置,直到存放元素成功。
4、ThreadLocal的get()和set()方法解釋
get():
獲取當前執行緒的ThreadLocalMap物件ThreadLocals;
從map中獲取執行緒儲存的Entry<K,V>節點;
從Entry<K,V>節點獲取儲存的Value副本值返回;
map為空的話返回初始值null,即執行緒變數副本為null,在使用時需要注意判斷NullPointerException。
set():
獲取當前執行緒的成員變數map;
map非空,則重新將ThreadLocal和新的value副本放入到map中;
map空,則對執行緒的成員變數ThreadLocalMap進行初始化建立,並將ThreadLocal和value副本放入map中。
5、ThreadLocal的問題,會造成記憶體洩露
ThreadLocalMap中的鍵是弱引用的,而value是強引用的,這樣會導致ThreadLocal在沒有外部物件強引用時,發生GC時弱引用key會被回收,而value不會被回收。此時建立ThreadLocal的執行緒還在執行,時間過長後,加之Entry物件的value一直得不到回收,發生記憶體洩露。
為此,在呼叫ThreadLocal的get()、set()後,需要呼叫remove()方法,將Entry節點和Map的引用關係溢位,這樣整個Entry物件在GC Roots分析後就變成不可達了,下次GC時就可以完成正確的回收。
6、ThreadLocal的應用場景
只適用於獨立副本的情況,例如資料庫連線、Session管理等