問題提出:當我們在一個多線程的環境中使用了全局變量,這個全局變量就存在一定被篡改的風險;為了避免共享變量的使用這種風險;我們提出了ThreadLocal輔助類為每一個線程提供各自的實例;
1:什么是ThreadLocal?
ThreadLocal是java.lang下面的一個類,是用來解決java多線程程序中并發問題的一種途徑;通過為每一個線程創建一份共享變量的副本來保證各個線程之間的變量的訪問和修改互相不影響;
2:它都有哪些方法:
public T get() { }
get方法用來獲取當前先線程中的共享變量的副本
public void set(T value) { }、
set是用來設置當前線程中變量的副本值
public void remove() { }
remove是用來移除當前線程中變量的副本,回收內存;其實這個不是必須要調用的,因為當線程失效之后,這些內存會自動釋放
protected T initialValue() { }
是一個protected方法;一般用于在使用時進行重寫;是一個延遲加載的方法;
3:它具體是怎么為每一個線程創建一個副本的呢?
A:首先它會先調用當前線程的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();
}
這里我們在來看看getMap的源碼:返回當前線程的ThreadLocalMap對象;
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocalMap是ThreadLocal的靜態內部類;
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
.....
}
可以看到Entry也是ThreadLocalMap的靜態內部類;
它的構造方法傳遞兩個參數一個是ThreadLocal一個Object;相當于鍵值對;
如果返回的ThreadLocalMap類中有值,獲取當前對象map對象的實體entry;
如果entry不為空則返回entry的值;否則調用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不為空就去設置map如果map為空就創建一個map
void createMap(Thread t, T firstValue) {
//這里的this指代的當前的ThreadLocal對象;
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
下面我們就來總結ThreadLocal具體是怎么一步一步去為每一個線程創建一個變量的副本的:
A:首先,在每個線程Thread內部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實際的變量副本的,鍵值為當前ThreadLocal變量,值value為變量副本(即T類型的變量)。
B:初始時,在Thread里面,threadLocals為空,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,并且以當前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。
C:然后在當前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查找。
總結一下:
1)實際的通過ThreadLocal創建的副本是存儲在每個線程自己的threadLocals中的;
2)為何threadLocals的類型ThreadLocalMap的鍵值為ThreadLocal對象,因為每個線程中可有多個threadLocal變量,就像上面代碼中的longLocal和stringLocal;
3)在進行get之前,必須先set,否則會報空指針異常;
如果想在get之前不需要調用set就能正常訪問的話,必須重寫initialValue()方法;
4:ThreadLocal的應用場景:
數據庫連接和session管理
文章大部分內容引自:
作者:海子
出處:http://www.cnblogs.com/dolphin0520/