ThreadLocal的原理核心就是理順Thread,ThreadLocal以及ThreadLocalMap三者之間的關係。即每個Thread對象中都持有一個ThreadLocalMap成員變量,ThreadLocalMap可以持有多個ThreadLocal和對應的value。當往ThreadLocalMap 設置值時候,key是threadLocal本身。
下面,通過對其核心的三個方法的流程來理解其實現過程。
initialValue()方法處理流程分析
initialValue()方法會返回當前線程對應的“初始值”,這是一個延遲加載的方法,只有在調用get的時候,才會觸發當線程第一次使用get方法訪問變量時,將調用此方法。但是如果線程調用了set方法,在這種情況下,不會調用initialValue方法了。
<code>protected T initialValue() {
return null;
}/<code>
默認的initialValue方法是沒做任何事情的,所以我們要重寫initialValue方法,就像前面的例子。
Set流程分析
Set方法是設置值。
Threadlocal.set()
<code>public void set(T value) {
//獲取當前線程
Thread t = Thread.currentThread();
//通過當前線程,獲取當前線程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//如果ThreadLocalMap已經存在,則將值傳進去
map.set(this, value);
else
//如果ThreadLocalMap不存在,則創建ThreadLocalMap
createMap(t, value);
}/<code>
這裡設置值是設置到Thread的ThreadLocalMap中,所以需要獲取當前的Thread。
Threadlocal.getMap方法
<code>ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}/<code>
這個方法很簡單,就是獲取線程中的成員變量ThreadLocalMap
在Thread中成員變量threadLocals就是ThreadLocal.ThreadLocalMap 類型,初始值為null。
Threadlocal.createMap方法
<code>void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}/<code>
可以看到,createMap方法就是為Thread的成員變量threadLocals 創建實例。並將需要設置的值放進map中。key就是threadlocal本身,value是需要保存的值。
Get流程分析
Get獲取設置的值
ThreadLocal.get方法
<code>public T get() {
//獲取當前的線程
Thread t = Thread.currentThread();
//通過當前線程,獲取當前線程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//如果ThreadLocalMap不為空,獲取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果ThreadLocalMap為空,返回初始值
return setInitialValue();
}/<code>
這裡要注意的是Entry 繼承了弱引用類型,使用弱引用類型的目的是為了避免已經無用的數據佔用內存空間。當一個對象僅僅被weak reference指向, 而沒有任何其他強引用指向, GC運行時候這個對象就會被回收。
Threadlocal.Entry 類型
<code>static class Entry extends WeakReference<threadlocal>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}/<threadlocal>/<code>
<code>弱引用也是關鍵的知識。下面的代碼可以測試弱引用是否有強引用時候的區別
public class TestWeakReference {
public static void main(String[] args) {
Car car = new Car(22000,"silver");
WeakReferenceweakCar = new WeakReference /<code>(car);
int i=0;
while(true){
if(weakCar.get()!=null){
i++;
System.out.println("Object is alive for "+i+" loops - "+weakCar);
}else{
System.out.println("Object has been collected.");
break;
}
}
//如果有下面這一句,car就不會被回收。因為被強應用指向了
//System.out.println(car);
}
}
如果註釋掉打印“System.out.println(car)”這一行,過了一段時間GC後,會獲取到null的car值,然後會打印“Object has been collected.”
閱讀更多 IT技術研習社 的文章
關鍵字: 源碼 initialValue null