嘮點面試官愛聽的系列之ThreadLocal
面試官:“看你簡歷寫了熟悉 Java 并發編程,那你給我講講 ThreadLocal 吧。”
我:“ThreadLocal 是 Java 中的一個類,用于創建線程局部變量。每個線程對 ThreadLocal 變量的訪問都是獨立的,每個線程都會擁有自己獨立的副本。”
完蛋,這么嘮,面試官一看就是背的八股,一點自己的東西都沒有。
你得嘮點面試官愛聽的,要有一定深度,讓面試官看出自己的思考。
下面我先來打個樣。
我在實際開發項目有利用過 ThreadLocal 存儲用戶信息,并對 ThreadLocal 源碼有過一定研究。
ThreadLocal 的優勢是無鎖化提升并發性能和簡化變量的傳遞邏輯,每個線程對 ThreadLocal 變量的訪問都是獨立的,每個線程都會擁有自己獨立的副本。
我:“需要我展開聊聊嗎?”
面試官:“好的。”
ThreadLocal 存儲的變量實際上是存儲在 Thread 線程對象中,在 Thread 類中有兩個 ThreadLocalMap 類型的變量一個是 threadLocals,另一個是 inheritableThreadLocals。
其中 threadLocals 就是用于存儲 ThreadLocal 對應的變量。ThreadLocalMap 也是哈希數據結構,不過與我們用的最多的 HashMap 有所不同。
ThreadLocalMap 解決哈希沖突采用的是線性探測法,而 HashMap 采用的是拉鏈法。
ThreadLocalMap 的初始容量是 16,當負載達到 2/3 的時候會觸發擴容邏輯,擴容的時候容量*2。
ThreadLocalMap 通過 Entry 數組存儲數據。每一個 Entry 對象的 key 是一個弱引用指向的 ThreadLocal 對象。值是一個強引用的對象,類型由 ThreadLocal 對象的泛型 <> 決定。
在 Entry 對象中 ThreadLocal 之所以使用弱引用進行鏈接是為了減少當內存泄露發生時所帶來的內存損失。
一旦 ThreadLocal 對象失去了外界強引用,在發生垃圾回收時僅被 Entry 對象弱引用的 ThreadLocal 對象就會被垃圾回收器回收,這時該 Entry 對象就是所謂的過時 Entry。
過時 Entry 自身及其引用的 vlaue 值在其它 ThreadLocal 對象執行 get、set、remove 方法時,可能會被清理,從而釋放泄露的內存。
當然在實際開發中,我們更應該主動在恰當時機調用 remove 方法,對不再使用的 ThreadLocal 對象進行清理,避免觸發 ThreadLocal 的清理機制,進而提升 get、set、remove 方法的執行效率。
需要注意的一點,我們通常所使用的 web 容器 tomcat 對工作線程的管理使用了池化技術,也就是說 Thread 對象會被重復使用。
如果我們在一個請求結束的時候沒有調用 remove 方法清理 ThreadLocal 對象,并且其他請求在執行 get 方法前沒有執行 set 方法進行設置值,那么可能會發生匪夷所思的業務異常。
咦?為什么當前是 A 發起的請求,獲取到的卻是 B 的信息?
圖片
ThreadLocal 類有一個子類叫做 InheritableThreadLocal,InheritableThreadLocal 主要解決在單次請求過程中涉及到了多線程異步處理邏輯時,ThreadLocal 變量無法傳遞問題。
雙十一有一個比價需求,需要拉取同商品在拼多多、淘寶、京東、抖音上的價格進行比價,如果采用單線程去執行,需要依次進行調用,該接口總耗時為獲取各大平臺價格耗時的累加和。
此時要想提高接口執行效率可以采取多線程方案,但是在獲取各大平臺優惠價時需要獲取 ThreadLocal 中存儲的上下文信息。
但是,我們前面說過 ThreadLocal 儲存的上下文,其實是存儲在了 Thread 對象中。
而采取多線程方案時,獲取各大平臺價格的線程并不是之前的主線程,這時是無法直接通過 ThreadLocal 的 get 方法獲取到在主線程存儲的上下文信息。
此時怎么辦呢?有兩種方案,第一種是在開啟多線程之前先取出上下文信息,然后作為參數傳遞給每一個線程,但是這不是與 ThreadLocal 簡化變量的傳遞邏輯的初衷相悖了嗎?
通過 InheritableThreadLocal 就可以很好的解決這個問題,在線程初始化的時候,當前線程的 inheritableThreadLocals 會拷貝給新創建線程的 inheritableThreadLocals。
inheritableThreadLocals 就是上文提到的 Thread 線程對象中的另外一個 ThreadLocalMap 類型變量,用于存儲 InheritableThreadLocal 記錄的信息。
圖片
我:“以上就是我對 ThreadLocal 的一些認識。”
內心甚至期盼著面試官繼續深究,看過源碼解讀系列完全不慌!
想要進行一步了解 ThreadLocal 的小伙伴可以回看 ThreadLocal 源碼解讀系列。