解析ThreadLocal

Real_man發表於2019-05-13

ThreadLocal是在開發中相對比較常見的工具類了,可以在多執行緒環境下保證執行緒安全,其基本原理就是給每一個物件都分配一個屬於當前執行緒的私有物件,這樣執行緒之間拿到的物件就不會相互衝突了。

可以想象成,如果多個人公用一個廁所的時候,因為廁所的一些限制,有很多的人都不能冒然的使用廁所,如果每一個人來的時候都給他一個單獨的廁所這樣相互之間就不會打擾了,缺點的話也很明顯可以看出來就是浪費資源。所以在用完的時候要記得銷燬資源。

有的人可能會問你再什麼時候下使用ThreadLocal呢?

  • 在Spring的事務中,預設內部用的是ThreadLocal給每一個執行緒分配一個單獨的連線,不過這是內部已經封裝好的了,不算是我們自己寫的
  • 在Web應用中,有的時候要對一些請求做單獨的處理,響應做額外的處理,每個人的寫法都不一樣,我有見過一些人在每一個請求的時候對應一個執行緒,並且對每個請求都有一些ThreadLocal相關的變數,儲存請求的資訊,以便在後續的處理中再從ThreadLocal中去拿
  • 比較常見的例子,應該是SimpleDateFormat了,這個物件在多執行緒下會出現一定的問題,一般在高併發的場景下,都會使用ThreadLocal給每個執行緒分配一個SimpleDateFormat物件。

知道了原理在使用的時候考慮下,或者在對應的場景下想想能不能用ThreadLocal去做。

內部結構

只是知道了其基本使用的話,相對還是不夠,其內部的資料結構也很有意思,在看過之後才發現其中的巧妙。

  • 每個Thread物件內部都有一個ThreadLocal.ThreadLocalMap屬性,屬性名為threadLocals,在建立執行緒之後可以直接通過t.threadLocals訪問

image-20190513101303375

當呼叫ThreadLocal的set方法時:

  • 獲取當前Thread的ThreadLocalMap屬性,如果當前執行緒的ThreadLocalMap為空的話,會進行初始化並賦值,如果不為空,則直接賦值

當有多個ThreadLocal物件時候,其實每個執行緒內部的ThreadLocalMap屬性裡面的Entry陣列,對應的下標為ThreadLocal的hashCode進行取餘,然後再構建Entry物件。

其實就是一個執行緒內部可以儲存多個ThreadLocal給的執行緒私有物件,只不過ThreadLocal物件是訪問那些執行緒私有物件的入口。

記憶體洩漏問題

關於ThreadLocal如果不進行Remove是否會導致記憶體洩漏?

通過上面我們知道Thread中的ThreadLocalMap屬性中的Entry表都是弱引用的物件,其引用為ThreadLocal物件,那麼弱引用物件一般在進行垃圾回收的時候都會被回收掉。

ThreadLocal在沒有外部強引用時,發生GC時會被回收,如果建立ThreadLocal的執行緒一直持續執行,那麼這個Entry物件中的value就有可能一直得不到回收,發生記憶體洩露。

記得在使用ThreadLocal完畢之後,呼叫ThreadLocal.remove方法。

最後

ThreadLocal也算是一個比較經典的知識點了,通過它可以問出

  • 引用型別
  • Map的資料結構
  • 執行緒安全
  • 實際場景中的用法

希望對你有幫助

參考

相關文章