概念
ThreadLocal用於提供線程局部變量,在多線程環境可以保證各個線程裏的變量獨立於其他線程裏的變量。也就是說ThreadLocal 可以爲每個線程創建一個【單獨的變量副本】,相當於線程的 private static 類型變量。
ThreadLocal 的作用和同步機制有些相反:同步機制是爲了保證多線程環境下數據的一致性;而 ThreadLocal 是保證了多線程環境下數據的獨立性。
範例:ThreadLocal的簡單使用
public class test {
private static String commStr;
private static ThreadLocal<String> threadStr=new ThreadLocal<>();
public static void main(String[] args){
threadStr.set("Thread-main設置的值");
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
threadStr.set(Thread.currentThread().getName()+"設置的值");
System.out.println(Thread.currentThread().getName()+"輸出ThreadLocal的值:"+threadStr.get());
}
});
thread.setName("Thread-user");
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"輸出ThreadLocal的值:"+threadStr.get());
}
}
內部實現
ThreadLocal 是怎樣保證其值在各個線程中是獨立的呢?
set(T value)方法
首先獲取當前線程對象,然後獲取到當前線程的ThreadLocalMap,如果ThreadLocalMap不爲null,則將value保存到ThreadLocalMap中,並用當前ThreadLocal 作爲 key;否則創建一個ThreadLocalMap 並給到當前線程,然後保存 value。
ThreadLocalMap 相當於一個 HashMap,是真正保存值的地方。
get()方法
在 get() 方法中也會獲取到當前線程的 ThreadLocalMap,如果 ThreadLocalMap 不爲 null,則把獲取 key 爲當前 ThreadLocal 的值;否則調用 setInitialValue() 方法返回初始值,並保存到新創建的 ThreadLocalMap 中。
remove()方法
ThreadLocalMap
在 set,get,initialValue 和 remove 方法中都會獲取到當前線程,然後通過當前線程獲取到 ThreadLocalMap,如果
ThreadLocalMap 爲 null,則會創建一個 ThreadLocalMap,並給到當前線程。
在使用 ThreadLocal 類型變量進行相關操作時,都會通過當前線程獲取到 ThreadLocalMap 來完成操作。每個線程的
ThreadLocalMap 是 屬 於 線 程 自 己 的 , ThreadLocalMap 中 維 護 的 值 也 是 屬 於 線 程 自 己 的 。 這 就 保 證 了
ThreadLocal 類型的變量在每個線程中是獨立的,在多線程環境下不會相互影響。
構造方法
存儲結構
存儲對象Entry
內存泄露
在 ThreadLocalMap 的 set(),get() 和 remove() 方法中,都有清除無效 Entry 的操作,這樣做是爲了降低內存泄漏發
生的可能。
Entry 中的 key 使用了弱引用的方式,這樣做是爲了降低內存泄漏發生的概率,但不能完全避免內存泄漏。
假設 Entry 的 key 沒有使用弱引用的方式,而是使用了強引用:由於 ThreadLocalMap 的生命週期和當前線程一樣長,那麼當引用 ThreadLocal 的對象被回收後,由於ThreadLocalMap 還持有 ThreadLocal 和對應 value 的強引用,ThreadLocal 和對應的 value 是不會被回收的,這就導致了內存泄漏。所以 Entry 以弱引用的方式避免了 ThreadLocal
沒有被回收而導致的內存泄漏,但是此時 value 仍然是無法回收的,依然會導致內存泄漏。
ThreadLocalMap 已經考慮到這種情況,並且有一些防護措施:在調用 ThreadLocal 的 get(),set() 和 remove() 的時候都會清除當前線程 ThreadLocalMap 中所有 key 爲 null 的value。這樣可以降低內存泄漏發生的概率。所以我們在使用 ThreadLocal 的時候,每次用完 ThreadLocal 都調用 remove() 方法,清除數據,防止內存泄漏。