摘抄自https://www.cnblogs.com/coderxx/p/12043764.html
ThreadLoal 變量,它的基本原理是漩绵,同一個 ThreadLocal 所包含的對象(對ThreadLocal< StringBuilder >而言即為 StringBuilder 類型變量)旨巷,在不同的 Thread 中有不同的副本(實際上是不同的實例):
因為每個 Thread 內(nèi)有自己的實例副本,且該副本只能由當前 Thread 使用岔留;
既然其它 Thread 不可訪問胁赢,那就不存在多線程間共享的問題务唐。
ThreadLocal 提供了線程本地的實例。它與普通變量的區(qū)別在于又兵,每個使用該變量的線程都會初始化一個完全獨立的實例副本任柜。ThreadLocal 變量通常被private static修飾。當一個線程結(jié)束時沛厨,它所使用的所有 ThreadLocal 相對的實例副本都會被回收宙地。
因此ThreadLocal 非常適用于這樣的場景:每個線程需要自己獨立的實例且該實例需要在多個方法中使用。
ThreadLocalMap與內(nèi)存泄漏
在該方案中俄烁,Map 由 ThreadLocal 類的靜態(tài)內(nèi)部類 ThreadLocalMap 提供绸栅。該類的實例維護某個 ThreadLocal 與具體實例的映射。與 HashMap 不同的是页屠,ThreadLocalMap 的每個 Entry 都是一個對 Key 的弱引用粹胯,這一點我們可以從super(k)
可看出。另外辰企,每個 Entry 中都包含了一個對 Value 的強引用风纠。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
之所以使用弱引用,是因為當沒有強引用指向 ThreadLocal 變量時牢贸,這個變量就可以被回收竹观,就避免ThreadLocal 因為不能被回收而造成的內(nèi)存泄漏的問題。
但是潜索,這里又可能出現(xiàn)另外一種內(nèi)存泄漏的問題臭增。ThreadLocalMap 維護 (key)ThreadLocal 變量與(value)具體實例的映射,當 ThreadLocal 變量被回收后竹习,該映射的鍵變?yōu)?null誊抛,該 Entry 無法被移除。從而使得實例被該 Entry 引用而無法被回收造成內(nèi)存泄漏整陌。
注意:Entry是對 ThreadLocal 類型的弱引用拗窃,并不是具體實例的弱引用瞎领,因此還存在具體實例相關的內(nèi)存泄漏的問題。
防止內(nèi)存泄漏
對于已經(jīng)不再被使用且已被回收的 ThreadLocal 對象随夸,它在每個線程內(nèi)對應的實例由于被線程的 ThreadLocalMap 的 Entry 強引用九默,無法被回收,可能會造成內(nèi)存泄漏宾毒。
針對該問題驼修,ThreadLocalMap 的 set 方法中,通過 replaceStaleEntry 方法將所有鍵為 null 的 Entry 的值設置為 null诈铛,從而使得該值可被回收邪锌。另外,會在 rehash 方法中通過 expungeStaleEntry 方法將鍵和值為 null 的 Entry 設置為 null 從而使得該 Entry 可被回收癌瘾。
總結(jié)
ThreadLocal 并不解決線程間共享數(shù)據(jù)的問題
ThreadLocal 通過隱式的在不同線程內(nèi)創(chuàng)建獨立實例副本避免了實例線程安全的問題
每個線程持有一個 Map 并維護了 ThreadLocal 對象與具體實例的映射,該 Map 由于只被持有它的線程訪問饵溅,故不存在線程安全以及鎖的問題
ThreadLocalMap 的 Entry 對 ThreadLocal 的引用為弱引用妨退,避免了 ThreadLocal 對象無法被回收的問題
ThreadLocalMap 的 set 方法通過調(diào)用 replaceStaleEntry 方法回收鍵為 null 的 Entry 對象的值(即為具體實例)以及 Entry 對象本身從而防止內(nèi)存泄漏
ThreadLocal 適用于變量在線程間隔離且在方法間共享的場景