本質(zhì)上,ThreadLocal是通過空間來換取時間,從而實現(xiàn)每個線程當(dāng)中都會有一個變量的副本,這樣每個線程就都會操作該副本,從而完全規(guī)避了多線程的并發(fā)問題。
Java中存在4種類型引用
1.強引用(strong) 如果一個對象被強停止引用所指向, 它不會被垃圾收集器回收
2.軟引用(soft) 當(dāng)內(nèi)存空間明顯不夠的情況,GC才會將軟引用所指向?qū)ο蠡厥?br>
3.弱引用(weak) 在下一次垃圾回收的情況下被回收
4.虛引用(phantom)
除了強引用用外, 其它要繼承Reference
重要: ThreadLocal中 Entry extends WeakReference 防止內(nèi)存泄露 (繼承WeakReference后, 若代碼寫得不正確也可能千萬內(nèi)存泄露)
棧: 引用在此上, 局部變量引用
堆: new出來的對象
public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal();
threadLocal.set("hello");
System.out.println(threadLocal.get()); //hello
threadLocal.set("jimmy");
System.out.println(threadLocal.get()); //jimmy
}
源碼
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;

https://www.cnblogs.com/jiangxinlingdu/p/11055540.html
ThreadLocal是什么
ThreadLocal是一個本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,在高并發(fā)場景下,可以實現(xiàn)無狀態(tài)的調(diào)用,特別適用于各個線程依賴不同的變量值完成操作的場景。
從數(shù)據(jù)結(jié)構(gòu)入手
下圖為ThreadLocal的內(nèi)部結(jié)構(gòu)圖

從上面的結(jié)構(gòu)圖,我們已經(jīng)窺見ThreadLocal的核心機制:
每個Thread線程內(nèi)部都有一個Map。
Map里面存儲線程本地對象(key)和線程的變量副本(value)
但是,Thread內(nèi)部的Map是由ThreadLocal維護的,由ThreadLocal負責(zé)向map獲取和設(shè)置線程的變量值。
所以對于不同的線程,每次獲取副本值時,別的線程并不能獲取到當(dāng)前線程的副本值,形成了副本的隔離,互不干擾。
ThreadLocalMap的問題
由于ThreadLocalMap的key是弱引用,而Value是強引用。這就導(dǎo)致了一個問題,ThreadLocal在沒有外部對象強引用時,發(fā)生GC時弱引用Key會被回收,而Value不會回收,如果創(chuàng)建ThreadLocal的線程一直持續(xù)運行,那么這個Entry對象中的value就有可能一直得不到回收,發(fā)生內(nèi)存泄露。
如何避免泄漏
既然Key是弱引用,那么我們要做的事,就是在調(diào)用ThreadLocal的get()、set()方法時完成后再調(diào)用remove方法,將Entry節(jié)點和Map的引用關(guān)系移除,這樣整個Entry對象在GC Roots分析后就變成不可達了,下次GC的時候就可以被回收。
如果使用ThreadLocal的set方法之后,沒有顯示的調(diào)用remove方法,就有可能發(fā)生內(nèi)存泄露,所以養(yǎng)成良好的編程習(xí)慣十分重要,使用完ThreadLocal之后,記得調(diào)用remove方法。
ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
try {
threadLocal.set(new Session(1, "Misout的博客"));
// 其它業(yè)務(wù)邏輯
} finally {
threadLocal.remove();
}
總結(jié)
每個ThreadLocal只能保存一個變量副本,如果想要上線一個線程能夠保存多個副本以上,就需要創(chuàng)建多個ThreadLocal。
ThreadLocal內(nèi)部的ThreadLocalMap鍵為弱引用,會有內(nèi)存泄漏的風(fēng)險。
適用于無狀態(tài),副本變量獨立后不影響業(yè)務(wù)邏輯的高并發(fā)場景。如果如果業(yè)務(wù)邏輯強依賴于副本變量,則不適合用ThreadLocal解決,需要另尋解決方案。