ThreadLocal源碼分析

參考:https://www.cnblogs.com/xzwblog/p/7227509.html

開局一張圖,內容全靠編?。。?/p>

ThreadLocal結構圖

ThreadLocal是什么?

官方描述如下:


* This class provides thread-local variables. These variables differ from

* their normal counterparts in that each thread that accesses one (via its

* {@code get} or {@code set} method) has its own, independently initialized

* copy of the variable.? {@code ThreadLocal} instances are typically private

* static fields in classes that wish to associate state with a thread (e.g.,

* a user ID or Transaction ID).


大意是指:ThreadLocal類提供線程局部變量,這種變量作用于線程的生命周期內。作用:提供一個線程內公共變量,減少同一個線程內多個函數(shù)或者組件之間一些公共變量的傳遞的復雜度,或者為線程提供一個私有的變量副本,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產(chǎn)生影響。

下面進行源碼分析:

1、先看構造函數(shù):

/**

* Creates a thread local variable.

* @see #withInitial(java.util.function.Supplier)

*/

public ThreadLocal() {

}

2、查看set()方法:

1、首先通過Thread.currentThread();獲取當前線程t


2、通過getMap()方法得到Thread類的成員變量threadLocals、它是ThreadLocal.ThreadLocalMap類型

public class Thread implements Runnable {

????/* ThreadLocal values pertaining to this thread. This map is maintained

????* by the ThreadLocal class.

????*/

ThreadLocal.ThreadLocalMap threadLocals = null;

}


ThreadLocalMap是一個自定義的HashMap,用于維護ThreadLocal的值。ThreadLocalMap使用ThreadLocal的弱引用作為Key,如果一個ThreadLocal沒有外部強引用引用他,那么系統(tǒng)GC的時候,這個ThreadLocal勢必會被回收,這樣一來,ThreadLocalMap中就會出現(xiàn)key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當前線程再遲遲不結束的話,這些key為null的Entry的value就會一直存在一條強引用鏈:

Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value

永遠無法回收,造成內存泄露。


ThreadLocalMap的設計對策:

1、首先從ThreadLocal的直接索引位置(通過ThreadLocal.threadLocalHashCode & (table.length-1)運算得到)獲取Entry e,如果e不為null并且key相同則返回e;

2、如果e為null或者key不一致則向下一個位置查詢,如果下一個位置的key和當前需要查詢的key相等,則返回對應的Entry。否則,如果key值為null,則擦除該位置的Entry,并繼續(xù)向下一個位置查詢。在這個過程中遇到的key為null的Entry都會被擦除,那么Entry內的value也就沒有強引用鏈,自然會被回收。仔細研究代碼可以發(fā)現(xiàn),set操作也有類似的思想,將key為null的這些Entry都刪除,防止內存泄露。

  但是光這樣還是不夠的,上面的設計思路依賴一個前提條件:要調用ThreadLocalMap的getEntry函數(shù)或者set函數(shù)。這當然是不可能任何情況都成立的,所以很多情況下需要使用者手動調用ThreadLocal的remove函數(shù),手動刪除不再需要的ThreadLocal,防止內存泄露。所以JDK建議將ThreadLocal變量定義成private static的,這樣的話ThreadLocal的生命周期就更長,由于一直存在ThreadLocal的強引用,所以ThreadLocal也就不會被回收,也就能保證任何時候都能根據(jù)ThreadLocal的弱引用訪問到Entry的value值,然后remove它,防止內存泄露。


總結set()方法:首先拿到當前的線程,然后拿到存儲在線程中的ThreadLocalMap對象(自定義的HashMap),如果map != null,把當前的ThreadLocal對象作為key,以及參數(shù)value,存儲到map中。如果map == null,則創(chuàng)建ThreadLocalMap對象。


創(chuàng)建一個ThreadLocalMap對象,然后把當前的ThreadLocal和firstValue作為鍵值對存儲進去并賦值給當前線程的threadLocalMap。

3、get()方法:


拿到當前的線程的ThreadLocalMap對象。進而通過ThreadLocalMap.Entry拿到對應的value。如果ThreadLocalMap == null,則調用setInitialValue()方法并返回value(null)

在ininitialValue()方法中返回了null,即 value == null;然后又是創(chuàng)建了一個ThreadLocalMap對象,并將<currentThread,null>丟了進去。

總結:

1、Thread類中有個成員變量屬于ThreadLocalMap類(一個定義在ThreadLocal類中的內部類),它是一個Map,key是當前的Thread的實例

2、當調用set(T t)時,首先獲取當前線程的ThreadLocalMap類屬性,然后以當前Thread的實例為key,t 為value,get值類似。

3、ThreadLocal變量的活動范圍為某線程,是改線程“專有、獨占”的,對該變量的所有操作均由該線程完成!也就是說,ThreadLocal 不是用來解決共享對象的多線程訪問的競爭問題的,因為ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。當線程終止后,這些值會作為垃圾回收。

4、ThreadLocal的工作原理決定了:每個線程獨自擁有一個變量,并非是可共享的。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • java版本是 java1.8.0_181 每個版本的具體實現(xiàn)細節(jié)都大同小異,接著從以下幾個問題進行分析。 為什...
    衣忌破閱讀 228評論 0 0
  • 1. 背景 ThreadLocal源碼解讀,網(wǎng)上面早已經(jīng)泛濫了,大多比較淺,甚至有的連基本原理都說的很有問題,包括...
    時之令閱讀 698評論 1 5
  • 首先通過問題去看源碼 ThreadLocal通過空間換取線程變量安全的說法正確嗎 ThreadLocal為什么說會...
    Visualing閱讀 277評論 0 2
  • 一. 簡介 提醒篇幅較大需耐心。 簡介來自ThreadLocal類注釋 ThreadLocal類提供了線程局部 (...
    BrightLoong閱讀 9,614評論 2 14
  • 1. ThreadLocal的作用 ThreadLocal的作用是提供線程內的局部變量,說白了,就是在各線程內部...
    maskwang520閱讀 516評論 0 1

友情鏈接更多精彩內容