Java面試官:ThreadLocal有何缺陷?

本文將循序漸進的方式爲大家介紹ThreadLocal方面的知識點,希望大家能夠理解透徹,在實戰與面試中能遊刃有餘!


本文目錄:
  • 介紹ThreadLocal概念
  • 介紹ThreadLocal實現原理
  • 內存泄漏問題
  • 最佳實踐

一、概念

  • 產生的訴求:每條線程都需要存取一個同名變量,但每條線程中該變量的值均不相同。
  • 思路:
    • 使用一個線程共享的Map<Thread,Object>,Map中的key爲線程對象,value即爲需要存儲的值。
    • 那麼,我們只需要通過map.get(Thread.currentThread())即可獲取本線程中該變量的值。
    • 問題:需要同步,效率低下,即便有juc包下的ConcurrentHashMap也只能降低鎖的粒度,效率依舊低下。

  • ThreadLocal就此誕生
    • 這個類顧名思義可以理解爲線程本地變量。
    • 也就是說每個線程往這個ThreadLocal中讀寫是線程隔離,互相之間不會影響的。它提供了一種將可變數據通過每個線程有自己的獨立副本從而實現線程封閉的機制。
    • demo
    •      
           
           
      public class MyTest {

      private static ThreadLocal<String> traceLocal = new ThreadLocal<>();

      public static void main(String[] args) {
      new Thread(() -> {
      traceLocal.set("123");
      System.out.println(traceLocal.get());
                  traceLocal.remove();
      }).start();
      new Thread(() -> {
      traceLocal.set("456");
      System.out.println(traceLocal.get());
                  traceLocal.remove();
      }).start();
      }

      }

二、實現原理

  • set
    • ThreadLocal先獲取當前線程,然後通過當前線程獲取ThreadLocalMap對象,然後對獲取的map對象進行判斷,若已存在,則直接覆蓋old value;否則直接新建一個map
  • get
    • ThreadLocal先獲取當前線程,然後根據獲取到的當前線程去取ThreadLocalMap對象,然後再通過當前ThreadLocal對象獲取,對應的value
  • remove
    • 首先是通過當前線程獲取ThreadLocalMap對象,然後remove掉當前的threadlocal


  • ThreadLocalMap
    • 每個Thread維護一個ThreadLocalMap變量threadLocals
    • 這個映射表的key是ThreadLocal實例本身,value是真正需要存儲的Object。
    • 也就是說ThreadLocal本身並不存儲值,它只是作爲一個key來讓線程從ThreadLocalMap獲取value。
    • ThreadLocalMap是使用ThreadLocal的弱引用作爲Key的

三、內存泄漏問題

  • 內存泄漏的過程
    • 因爲線程的ThreadLocalMap的key爲ThreadLocal的弱引用
    • 如果ThreadLocal沒有額外的強引用指向它
    • 那麼GC的時候這個ThreadLocal對象就會被回收
    • 線程的ThreadLocal對象的key就會變成null,value和value指向的對象還是強引用
    • 因爲ThreadLocal已經回收了,所以線程沒法再操作到自己保存的關於這個ThreadLocal的value,如果這個線程遲遲不結束的話,這個value就被內存泄漏了。。。

  • 爲什麼使用弱引用
    • 如果使用強引用,程序在ThreadLocal對象不再使用時顯示置爲null,但是因爲被ThreadLocalMap強引用了,所以對象還是無法被回收(但是其實這個時候程序已經希望ThreadLocal被回收了)
    • 所以弱引用是爲了解決內存泄漏
    • 但是弱引用使用不當又會導致產生新的內存泄漏情況

  • 對於弱引用可能產生的新的內存泄漏情況的處理設計
    • ThreadLocalMap的設計中已經考慮到這種情況,也加上了一些防護措施:在ThreadLocal的get(),set(),remove()的時候都會有清除線程ThreadLocalMap裏key爲null的value的邏輯

四、最佳實踐

  • 1.將ThreadLocal變量定義爲private static
  • 2.用完即remove()
  •     
        
        
    JDK建議將ThreadLocal變量定義爲private static的,
    這樣的話ThreadLocal的生命週期就更長,
    由於一直存在ThreadLocal的強引用,
    所以ThreadLocal就不會回收,
    也就能保證任何時候都能根據ThreadLocal的弱引用訪問到Entry的value值,
    然後remove,防止內存泄漏。

本公衆號後續將會連續爲大家介紹Java面試方面的重點知識點,你也可以在下方留言想要學習的知識點,我們將會爲您詳細介紹!

歡迎關注“豬哥Java”

本文分享自微信公衆號 - 豬哥Java(pig-python)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章