1. 程式人生 > >ThreadLocal 原理探究

ThreadLocal 原理探究

ThreadLocal 的作用:

This class provides thread-local variables. 提供了執行緒級別的本地變數。

ThreadLocal的原理:

  1. 每一個執行緒Thread內部維護了兩個package private級別的變數:
    • ThreadLocal.ThreadLocalMap threadLocals
    • ThreadLocal.ThreadLocalMap inheritableThreadLocals
  2. 每一個 ThreadLocal 物件有一個建立時生成唯一的 Id稱為:threadLocalHashCode

    ,訪問一個 ThreadLocal 變數的值,就是用threadLocalHashCode去 本執行緒 的 ThreadLocalMap 中查詢對應的值。

    private Entry getEntry(ThreadLocal<?> key) {
     int i = key.threadLocalHashCode 
     & (table.length - 1);
     Entry e = table[i];
     if (e != null && e.get() == key)
         return e;
     else
         return getEntryAfterMiss(key, i, e);
    }
  3. ThreadLocalMap 為一個靜態內部類,包含了一個Entity陣列(Entry[] table)來保持ThreadLocal的執行緒變數;

ThreadLocalMap的原理

  1. ThreadLocalMap是一個Hash Map,採用的是開放地址法而不是連結串列的方式解決衝突。
  2. Entry 是 WeakReference 的子類,這樣不再被使用的 ThreadLocal 可以被檢查出來並清除掉。
  3. 執行緒隔離的祕密,就在於ThreadLocalMap這個類。ThreadLocalMap是ThreadLocal類的一個靜態內部類,它實現了鍵值對的設定和獲取(對比Map物件來理解),每個執行緒中都有一個獨立的ThreadLocalMap副本,它所儲存的值,只能被當前執行緒讀取和修改。ThreadLocal類通過操作每一個執行緒特有的ThreadLocalMap副本,從而實現了變數訪問在不同執行緒中的隔離。因為每個執行緒的變數都是自己特有的,完全不會有併發錯誤。還有一點就是,ThreadLocalMap儲存的鍵值對中的鍵是this物件指向的ThreadLocal物件,而值就是你所設定的物件了。

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
       void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

神奇的 0x61c88647

待補充;

使用陷阱:

  1. ThreadLocal和執行緒池同時使用出現變數汙染;
    在使用執行緒池的過程中,由於執行緒是不斷的回收和利用故ThreadLocal在伺服器中也是被反覆利用的,在使用中如果不進行清查操作,很容易導致變數汙染。

問題:

❓疑惑1:既然ThreadLocal只維護一個值,每一個Thread中都儲存的是一個副本,為什麼ThreadLocalMap需要時一個數組而不是object呢?

原因:如果我們期望在一個Thread中儲存多個ThreadLocal變數怎麼辦呢?不可能改造Thread,新增一個Object物件吧,所以為了更方便的在同一個Thread中加入多個ThreadLocal變數,才引入了陣列結構,不同的ThreadLocal物件作為不同鍵,所以線上程的ThreadLocalMap物件中設定不同的值。

❓疑惑2:ThreadLocalMap為什麼採用開放地址的方法,而不採用連結串列來防止hash衝突?

擴充套件閱讀:
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/