參考:https://www.cnblogs.com/xzwblog/p/7227509.html
開局一張圖,內容全靠編?。。?/p>

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的工作原理決定了:每個線程獨自擁有一個變量,并非是可共享的。