ThreadLocal概念
ThreadLocal,線程局部變量。
ThreadLocal能夠存放一個線程級别的變量,其本身能夠被多個線程共享使用,并且又能夠達到線程安全的目的。作用就是在多線程環境下去保證成員變量的安全。
常用的方法:get()/set()/initialValue()/remove()
常用場景
- ThreadLocal最常用的地方就是為每個線程綁定一個資料庫連接配接,HTTP請求,使用者身份資訊等,這樣一個線程的所有調用到的方法都可以非常友善地通路這些資源。
- Hibernate的Session 工具類HibernateUtil
- 通過不同的線程對象設定Bean屬性,保證各個線程Bean對象的獨立性。
原理
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB9ENrpXTycGROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzIjMzEzMygTM0EjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
在每個線程Thread内部有一個ThreadLocalMap,這是用來存儲實際的變量副本的,鍵值key為目前ThreadLocal變量,value為變量副本。
初始時,在Thread裡面,ThreadLocalMap為空,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的ThreadLocalMap進行初始化,并且以目前ThreadLocal變量為鍵值,以ThreadLocal要儲存的副本變量為value,存到ThreadLocalMap。
然後在目前線程裡面,如果要使用副本變量,就可以通過get方法在ThreadLocalMap裡面查找。 一個Thread中隻有一個ThreadLocalMap,一個ThreadLocalMap中可以有多個ThreadLocal對象,其中一個ThreadLocal對象對應一個ThreadLocalMap中的一個Entry(即一個Thread可以依附有多個ThreadLocal對象)。
[ThreadLocal原理參考]: https://blog.csdn.net/dakaniu/article/details/80829079
項目中的案例
場景:
在寫公司的一個中間件項目時,需要寫一個接口供外部調用。通過攔截器對外部請求進行日志記錄,需要記錄外部請求封包,響應封包,請求耗費時間。記錄消耗時間時,考慮到多個線程之間的請求會串用局部變量,是以考慮使用ThreadLocal來包裝上面的變量。
private static ThreadLocal<Date> startTime = new ThreadLocal<>();
private static ThreadLocal<Date> endTime = new ThreadLocal<>();
private static ThreadLocal<String> requestThreadLocal = new ThreadLocal<>();
private static ThreadLocal<String> responseThreadLocal = new ThreadLocal<>();
//在擷取到開始時間和結束時間後,進行相減,得到耗費時間
//......
項目上生産後,發現很多請求日志的耗費時間顯示為負數。進行分析後,發現是ThreadLocal出現了記憶體洩漏的原因,如果線程被重複使用了,就會使得之前在ThreadLocal中的變量會被重複利用,導緻了不同請求的時間串用了。
解決方案:
在每個線程對ThreadLocal中的變量每一次使用完畢後,都要使用ThreadLocal的remove()方法來清除掉這個線程的變量,這樣就解決了問題。
其他注意點:
ThreadLocal使用時,最好要加static修飾,避免過多建立,浪費資源。