ThreadLocal 是 Java 中實現線程封閉(Thread Confinement)的核心機制,它通過為每個線程創建變量的獨立副本來解決多線程環境下的線程安全問題。?
Thread
└── ThreadLocalMap (threadLocals) // 每個線程持有的專屬Map├── Entry[] table // 哈希表結構│ └── Entry extends WeakReference<ThreadLocal<?>>│ ├── ThreadLocal<?> key // 弱引用Key│ └── Object value // 強引用Value└── 其他HashMap類似字段
threadlocal里的數據是怎么存儲的?
每個線程內部有一個專屬、私有的ThreadLocalMap(就是一個hashMap,底層用Entry數組存數據)
value:要保存的資源
調用set方法時,就是以當前ThreadLocal對象為key,以資源為value,放到ThreadLocalMap中
調用get方法時,就是以當前ThreadLocal對象為key,從ThreadLocalMap中取value
開發中一般把ThreadLocal聲明為static,這樣ThreadLocal對象是線程共享的,那key都共享了,不同線程取出來的value不就是同一個嗎?
并不是。ThreadLocalMap是每個線程私有的
[正確模型]
static ThreadLocal實例(作為Key)
├── 線程1的ThreadLocalMap: Entry(Key=ThreadLocal實例 → ValueA)
├── 線程2的ThreadLocalMap: Entry(Key=ThreadLocal實例 → ValueB)
└── 線程3的ThreadLocalMap: Entry(Key=ThreadLocal實例 → ValueC)
為什么key是弱應用?
要避免key的內存泄露問題
情況1:new Thread()的情況,用完正常銷毀,那么ThreadLocalMap會被GC正常回收,不會出現內存泄露。
情況2:使用線程池結合ThreadLocal的情況(這個使用更常見,不需要頻繁創建,可以復用),線程池中的線程不會銷毀會一致復用。
弱引用Key,GC時ThreadLocal無強引用,回收Key防止Map持續增長
key只有弱引用時,只要發生了垃圾回收,ThreadLocalMap就會回收,避免內存泄露問題
ThreadLocalMap中如何清理大量為null的數據 ?
在調用set、get、remove方法時,就會遍歷數組,清除為null的entry,然后通過線性探測重新處理hash沖突
為什么value是強引用?
將數據存到value中,是為了要用它,如果不是強引用,就會被GC回收
那value怎么回收呢?
不回收會導致內存泄露
所以,在使用完ThreadLocal后必須斷開強引用,即調用remove方法手動斷開強引用,讓gc把它回收了