點擊上方 Java後端,選擇 設爲星標html
優質文章,及時送達java
ThreadLocal的原理和實現
ThreadLoal 變量,線程局部變量,同一個 ThreadLocal 所包含的對象,在不一樣的 Thread 中有不一樣的副本。ThreadLocal 變量一般被private static修飾。當一個線程結束時,它所使用的全部 ThreadLocal 相對的實例副本均可被回收。web
一個線程內能夠存在多個 ThreadLocal 對象,因此實際上是 ThreadLocal 內部維護了一個 Map ,這個 Map 不是直接使用的 HashMap ,而是 ThreadLocal 實現的一個叫作 ThreadLocalMap 的靜態內部類。面試
而咱們使用的 get()、set() 方法其實都是調用了這個ThreadLocalMap類對應的 get()、set() 方法。這個儲值的Map並不是ThreadLocal的成員變量,而是java.lang.Thread 類的成員變量。ThreadLocalMap實例是做爲java.lang.Thread的成員變量存儲的,每一個線程有惟一的一個threadLocalMap。數據庫
這個map以ThreadLocal對象爲key,」線程局部變量」爲值,因此一個線程下能夠保存多個」線程局部變量」。對ThreadLocal的操做,實際委託給當前Thread,每一個Thread都會有本身獨立的ThreadLocalMap實例,存儲的倉庫是Entry[] table;Entry的key爲ThreadLocal,value爲存儲內容;所以在併發環境下,對ThreadLocal的set或get,不會有任何問題。後端
因爲Tomcat線程池的緣由,我最初使用的」線程局部變量」保存的值,在下一次請求依然存在(同一個線程處理),這樣每次請求都是在本線程中取值。因此在線程池的狀況下,處理完成後主動調用該業務treadLocal的remove()方法,將」線程局部變量」清空,避免本線程下次處理的時候依然存在舊數據。微信
ThreadLocal爲何要使用弱引用和內存泄露問題數據結構
在ThreadLocal中內存泄漏是指ThreadLocalMap中的Entry中的key爲null,而value不爲null。由於key爲null致使value一直訪問不到,而根據可達性分析致使在垃圾回收的時候進行可達性分析的時候,value可達從而不會被回收掉,可是該value永遠不能被訪問到,這樣就存在了內存泄漏。架構
若是 key 是強引用,那麼發生 GC 時 ThreadLocalMap 還持有 ThreadLocal 的強引用,會致使 ThreadLocal 不會被回收,從而致使內存泄漏。弱引用 ThreadLocal 不會內存泄漏,對應的 value 在下一次 ThreadLocalMap 調用 set、get、remove 方法時被清除,這算是最優的解決方案。併發
Map中的key爲一個threadlocal實例.若是使用強引用,當ThreadLocal對象(假設爲ThreadLocal@123456)的引用被回收了,ThreadLocalMap自己依然還持有ThreadLocal@123456的強引用,若是沒有手動刪除這個key,則ThreadLocal@123456不會被回收,因此只要當前線程不消亡,ThreadLocalMap引用的那些對象就不會被回收,能夠認爲這致使Entry內存泄漏。
若是使用弱引用,那指向ThreadLocal@123456對象的引用就兩個:ThreadLocal強引用和ThreadLocalMap中Entry的弱引用。一旦ThreadLocal強引用被回收,則指向ThreadLocal@123456的就只有弱引用了,在下次gc的時候,這個ThreadLocal@123456就會被回收。
雖然上述的弱引用解決了key,也就是線程的ThreadLocal能及時被回收,可是value卻依然存在內存泄漏的問題。當把threadlocal實例置爲null之後,沒有任何強引用指向threadlocal實例,因此threadlocal將會被gc回收.map裏面的value卻沒有被回收.而這塊value永遠不會被訪問到了.
因此存在着內存泄露,由於存在一條從current thread鏈接過來的強引用.只有當前thread結束之後, current thread就不會存在棧中,強引用斷開, Current Thread, Map, value將所有被GC回收.因此當線程的某個localThread使用完了,立刻調用threadlocal的remove方法,就不會發生這種狀況了。
另外其實只要這個線程對象及時被gc回收,這個內存泄露問題影響不大,但在threadLocal設爲null到線程結束中間這段時間不會被回收的,就發生了咱們認爲的內存泄露。最要命的是線程對象不被回收的狀況,這就發生了真正意義上的內存泄露。好比使用線程池的時候,線程結束是不會銷燬的,會再次使用,就可能出現內存泄露。
最近整理一份面試資料《Java技術棧學習手冊》,覆蓋了Java技術、面試題精選、Spring全家桶、Nginx、SSM、微服務、數據庫、數據結構、架構等等。 獲取方式:點「 在看,關注公衆號 Java後端 並回復 777 領取,更多內容陸續奉上。 推 薦 閱 讀 1. 每日一題之 ZooKeeper 2. 每日一題之 Redis 3. 每日一題之 MySQL
4. 每日一題之 JVM-01
5. 每日一題之 JVM-02
6 .
每日一題之 JVM-03
7.
每日一題之線程
8.
每日一題之 HashMap
9. 每日一題之 HashMap - 02
![]()
喜歡文章,點個 在看 ![]()
本文分享自微信公衆號 - Java後端(web_resource)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。