問題提出:當(dāng)我們在一個(gè)多線程的環(huán)境中使用了全局變量的畴,這個(gè)全局變量就存在一定被篡改的風(fēng)險(xiǎn)硫眨;為了避免共享變量的使用這種風(fēng)險(xiǎn)足淆;我們提出了ThreadLocal輔助類為每一個(gè)線程提供各自的實(shí)例;
1:什么是ThreadLocal?
ThreadLocal是java.lang下面的一個(gè)類礁阁,是用來解決java多線程程序中并發(fā)問題的一種途徑巧号;通過為每一個(gè)線程創(chuàng)建一份共享變量的副本來保證各個(gè)線程之間的變量的訪問和修改互相不影響;
2:它都有哪些方法:
public T get() { }
get方法用來獲取當(dāng)前先線程中的共享變量的副本
public void set(T value) { }姥闭、
set是用來設(shè)置當(dāng)前線程中變量的副本值
public void remove() { }
remove是用來移除當(dāng)前線程中變量的副本丹鸿,回收內(nèi)存;其實(shí)這個(gè)不是必須要調(diào)用的棚品,因?yàn)楫?dāng)線程失效之后靠欢,這些內(nèi)存會自動釋放
protected T initialValue() { }
是一個(gè)protected方法;一般用于在使用時(shí)進(jìn)行重寫铜跑;是一個(gè)延遲加載的方法门怪;
3:它具體是怎么為每一個(gè)線程創(chuàng)建一個(gè)副本的呢?
A:首先它會先調(diào)用當(dāng)前線程的get方法锅纺;源碼如下:
public T get() {
Thread t = Thread.currentThread();//獲取當(dāng)前線程
ThreadLocalMap map = getMap(t);//
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
這里我們在來看看getMap的源碼:返回當(dāng)前線程的ThreadLocalMap對象掷空;
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類;
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
.....
}
可以看到Entry也是ThreadLocalMap的靜態(tài)內(nèi)部類;
它的構(gòu)造方法傳遞兩個(gè)參數(shù)一個(gè)是ThreadLocal一個(gè)Object;相當(dāng)于鍵值對坦弟;
如果返回的ThreadLocalMap類中有值护锤,獲取當(dāng)前對象map對象的實(shí)體entry;
如果entry不為空則返回entry的值酿傍;否則調(diào)用setInitialValue();
再來看看setInitialValue()方法的源碼:
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;
}
如果map不為空就去設(shè)置map如果map為空就創(chuàng)建一個(gè)map
void createMap(Thread t, T firstValue) {
//這里的this指代的當(dāng)前的ThreadLocal對象烙懦;
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
下面我們就來總結(jié)ThreadLocal具體是怎么一步一步去為每一個(gè)線程創(chuàng)建一個(gè)變量的副本的:
A:首先,在每個(gè)線程Thread內(nèi)部有一個(gè)ThreadLocal.ThreadLocalMap類型的成員變量threadLocals拧粪,這個(gè)threadLocals就是用來存儲實(shí)際的變量副本的修陡,鍵值為當(dāng)前ThreadLocal變量沧侥,值value為變量副本(即T類型的變量)可霎。
B:初始時(shí),在Thread里面宴杀,threadLocals為空癣朗,當(dāng)通過ThreadLocal變量調(diào)用get()方法或者set()方法,就會對Thread類中的threadLocals進(jìn)行初始化旺罢,并且以當(dāng)前ThreadLocal變量為鍵值旷余,以ThreadLocal要保存的副本變量為value,存到threadLocals扁达。
C:然后在當(dāng)前線程里面正卧,如果要使用副本變量,就可以通過get方法在threadLocals里面查找跪解。
總結(jié)一下:
1)實(shí)際的通過ThreadLocal創(chuàng)建的副本是存儲在每個(gè)線程自己的threadLocals中的炉旷;
2)為何threadLocals的類型ThreadLocalMap的鍵值為ThreadLocal對象,因?yàn)槊總€(gè)線程中可有多個(gè)threadLocal變量叉讥,就像上面代碼中的longLocal和stringLocal窘行;
3)在進(jìn)行g(shù)et之前,必須先set图仓,否則會報(bào)空指針異常罐盔;
如果想在get之前不需要調(diào)用set就能正常訪問的話,必須重寫initialValue()方法;
4:ThreadLocal的應(yīng)用場景:
數(shù)據(jù)庫連接和session管理
文章大部分內(nèi)容引自:
作者:海子
出處:http://www.cnblogs.com/dolphin0520/