詳細介紹ThreadLocal
JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優(yōu)美的多線程程序,ThreadLocal并不是一個Thread,而是Thread的局部變量。
一、ThreadLocal概述
學習JDK中的類,首先看下JDK API對此類的描述,描述如下:
JDK API 寫道
該類提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對應物,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立于變量的初始化副本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態(tài)與某一個線程(例如,用戶 ID 或事務 ID)相關(guān)聯(lián)。
API表達了下面幾種觀點:
1、ThreadLocal不是線程,是線程的一個變量,你可以先簡單理解為線程類的屬性變量。
2、ThreadLocal 在類中通常定義為靜態(tài)類變量。
3、每個線程有自己的一個ThreadLocal,它是變量的一個‘拷貝’,修改它不影響其他線程。
既然定義為類變量,為何為每個線程維護一個副本(姑且成為‘拷貝’容易理解),讓每個線程獨立訪問?多線程編程的經(jīng)驗告訴我們,對于線程共享資源(你可以理解為屬性),資源是否被所有線程共享,也就是說這個資源被一個線程修改是否影響另一個線程的運行,如果影響我們需要使用synchronized同步,讓線程順序訪問。
ThreadLocal適用于資源共享但不需要維護狀態(tài)的情況,也就是一個線程對資源的修改,不影響另一個線程的運行;這種設(shè)計是‘空間換時間’,synchronized順序執(zhí)行是‘時間換取空間’。
二、ThreadLocal方法介紹
- T get() 返回此線程局部變量的當前線程副本中的值。
- protected T initialValue() 返回此線程局部變量的當前線程的“初始值”。
- void remove() 移除此線程局部變量當前線程的值。
- void set(T value) 將此線程局部變量的當前線程副本中的值設(shè)置為指定值。
三、深入源碼
ThreadLocal有一個ThreadLocalMap靜態(tài)內(nèi)部類,你可以簡單理解為一個MAP,這個‘Map’為每個線程復制一個變量的‘拷貝’存儲其中。
當線程調(diào)用ThreadLocal.get()方法獲取變量時,首先獲取當前線程引用,以此為key去獲取響應的ThreadLocalMap,如果此‘Map’不存在則初始化一個,否則返回其中的變量,代碼如下:
Get方法代碼 :
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null)
- return (T)e.value;
- }
- return setInitialValue();
- }
調(diào)用get方法如果此Map不存在首先初始化,創(chuàng)建此map,將線程為key,初始化的vlaue存入其中,注意此處的initialValue,我們可以覆蓋此方法,在首次調(diào)用時初始化一個適當?shù)闹怠etInitialValue代碼如下:
Java代碼
- private T setInitialValue() {
- T value = initialValue();
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- return value;
- }
set方法相對比較簡單如果理解以上倆個方法,獲取當前線程的引用,從map中獲取該線程對應的map,如果map存在更新緩存值,否則創(chuàng)建并存儲,代碼如下:
Java代碼:
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
對于ThreadLocal在何處存儲變量副本,我們看getMap方法:獲取的是當前線程的ThreadLocal類型的threadLocals屬性。顯然變量副本存儲在每一個線程中。
Java代碼 :
- /**
- * 獲取線程的ThreadLocalMap 屬性實例
- */
- ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
- }
上面我們知道變量副本存放于何處,這里我們簡單說下如何被java的垃圾收集機制收集,當我們不在使用是調(diào)用set(null),此時不在將引用指向該‘map’,而線程退出時會執(zhí)行資源回收操作,將申請的資源進行回收,其實就是將屬性的引用設(shè)置為null。這時已經(jīng)不在有任何引用指向該map,故而會被垃圾收集。
希望通過本文對hreadLocal的介紹,能夠給你帶來幫助。