本地執行緒變數ThreadLocal
1.ThreadLocal是什麼 ThreadLocal是一個本地執行緒副本變數工具類。主要用於將私有執行緒和該執行緒存放的副本物件做一個對映,各個執行緒之間的變數互不干擾,在高併發場景下,可以實現無狀態的呼叫,特別適用於各個執行緒依賴不通的變數值完成操作的場景。 2.資料結構 1.每個thread執行緒內部都有一個map(threadLocalMap). 2.map裡面儲存執行緒本地物件(key)和執行緒的副本(value) 3.但是,thread內部的map是由threadLocal維護的,由threadlocal負責向map獲取和設定執行緒的變數值。 (所以對於不同的執行緒,每次獲取副本值時,別的執行緒並不能獲取到當前執行緒的副本值,形成了副本的隔離,互不干擾。)
3.方法 public T get():獲取當前執行緒的副本變數值。 public void set(T value):設定當前執行緒的副本變數值。 protected T initialValue() :為當前執行緒初始副本變數值。 public void remove():移除當前前程的副本變數值。 4.hash衝突怎麼解決 ThreadLocalMap解決Hash衝突的方式就是簡單的步長加1或減1,尋找下一個相鄰的位置。 (和HashMap的最大的不同在於,ThreadLocalMap結構非常簡單,沒有next引用,也就是說ThreadLocalMap中解決Hash衝突的方式並非連結串列的方式,而是採用線性探測的方式,所謂線性探測,就是根據初始key的hashcode值確定元素在table陣列中的位置,如果發現這個位置上已經有其他key值的元素被佔用,則利用固定的演算法尋找一定步長的下個位置,依次判斷,直至找到能夠存放的位置。) 5.threadLocalMap的問題 由於ThreadLocalMap的key是弱引用,而Value是強引用。這就導致了一個問題,ThreadLocal在沒有外部物件強引用時,發生GC時弱引用Key會被回收,而Value不會回收,如果建立ThreadLocal的執行緒一直持續執行,那麼這個Entry物件中的value就有可能一直得不到回收,發生記憶體洩露。
如何避免洩漏: 在呼叫ThreadLocal的get()、set()方法時完成後再呼叫remove方法,將Entry節點和Map的引用關係移除,這樣整個Entry物件在GC Roots分析後就變成不可達了,下次GC的時候就可以被回收。
6.總結 1.每個ThreadLocal只能儲存一個變數副本,如果想要上線一個執行緒能夠儲存多個副本以上,就需要建立多個ThreadLocal。 2.ThreadLocal內部的ThreadLocalMap鍵為弱引用,會有記憶體洩漏的風險。 3.用於無狀態,副本變數獨立後不影響業務邏輯的高併發場景。如果如果業務邏輯強依賴於副本變數,則不適合用ThreadLocal解決,需要另尋解決方案。