深入理解線程本地變量ThreadLocal
ThreadLocal理解:
如果在多線程并發環境中,一個可變對象涉及到共享與競爭,那么該可變對象就一定會涉及到線程間同步操作,這是多線程并發問題。
否則該可變對象將作為線程私有對象,可通過ThreadLocal進行管理,實現線程間私有對象隔離的目的。
可以發現,ThreadLocal并沒有解決多線程并發的問題,因為ThreadLocal管理的可變對象的性質本來就不會涉及到多線程并發而引發的共享、競爭和同步問題,使用ThreadLocal管理只是方便了多線程獲取和使用該私有可變對象的途徑和方式。
ThreadLocal解決的問題:
將一個公共的可變對象,轉換為線程私有的可變對象,防止出現不正確的共享。
《java并發變成實踐》
線程本地(ThreadLocal)變量通常用于防止在基于可變的單體(Singleton)或全局變量的設計中,出現(不正確的)共享。
ThreadLocal的使用場景:
一個可變對象,在每一個線程(或者說多線程環境)中都會被使用,并且,該可變對象無需在各個線程間進行同步,那么,該可變對象就可以通過ThreadLocal進行管理,而此時該可變對象變成多實例對象,與線程一一對應。
ThreadLocal主要方法:
-
public T get() { }返回當前線程所對應的線程局部變量
-
public void set(T value) { }設置當前線程的線程局部變量的值
-
public void remove(){} 將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。
-
protected T initialValue() { }返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。
ThreadLocal源碼分析:
get方法:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
//獲取當前線程對象
Thread t = Thread.currentThread();
//從當前線程對象中獲取ThreadLocalMap類型實例對象
ThreadLocalMap map = getMap(t);
if (map != null) {
//this對象,也就是ThreadLocal對象作為map的key,獲取ThreadLocal管理的可變對象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//map為空,延遲執行初始化方法
return setInitialValue();
}</code></pre>
看一下getMap(t)方法:
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
補充解釋:
1、ThreadLocalMap,是ThreadLocal的靜態內部類
2、ThreadLocalMap把其外部類ThreadLocal的實例對象作為key,把要管理的可變對象作為value
3、ThreadLocalMap的實例對象,由當前線程對象Thread的實例持有,而不是由ThreadLocal持有
如下圖源碼截圖進行證明:

然后繼續看setInitialValue()方法和initialValue()方法:
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
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;
}
protected T initialValue() {
return null;
}
補充說明:
1、initialValue()方法聲明為protected,目的就是讓子類覆蓋重新的,如果不覆蓋重寫,則返回null
2、如果沒有重寫initialValue()方法,ThreadLocal對象直接調用get()方法,最終從setInitialValue()返回的對象為null
看一下createMap(t, value)方法:
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
//直接new一個新的ThreadLocalMap實例,封裝進入當前線程對象t
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
從上面代碼可以發現,每一個線程對象都有一個專屬自己的ThreadLocalMap對象,而ThreadLocalMap對象存儲了ThreadLocal對象與變量對象。線程對象、ThreadLocalMap對象、ThreadLocal對象、變量對象之間的關系如下圖:

最后看一下set(T value)方法:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
補充解釋:
1、set方法用來修改或者初始化ThreadLocal管理的變量對象。
2、ThreadLocal對象調用get方法獲取變量的,要么重寫initialValue()方法,要么主動調用set方法,否則將返回null。
參考:
http://blog.csdn.net/lufeng20/article/details/24314381 (此文爭議比較大,但依然可以作為理解ThreadLocal的素材)
http://www.cnblogs.com/dolphin0520/p/3920407.html http://www.iteye.com/topic/103804
來自:http://blog.csdn.net/javaloveiphone/article/details/58600694