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

本地執行緒變數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解決,需要另尋解決方案。