前言
java內(nèi)存模型
java虛擬機,模仿真實的計算機結(jié)構(gòu)盐杂,為java中各個變量漏麦、實例分配內(nèi)存空間。主要有:
- 方法區(qū):類信息况褪、常量撕贞、靜態(tài)變量、即時編譯器編譯后的代碼數(shù)據(jù)测垛。方法區(qū)包括運行時常量池:字面量捏膨、符號引用
- 堆:線程共享的內(nèi)存區(qū)域,存放對象實例
- 程序計數(shù)器:
- 虛擬機棧:線程私有數(shù)據(jù)區(qū)域食侮,每個方法執(zhí)行時都會創(chuàng)建一個棧幀号涯,存儲方法的變量表、操作數(shù)棧锯七、動態(tài)鏈接链快、返回值、返回地址等信息眉尸。
- 本地方法棧:線程私有數(shù)據(jù)區(qū)域域蜗,與native代碼相關(guān)巨双。
ThreadLocal在每個線程中對該變量會創(chuàng)建一個副本,即每個線程內(nèi)部都會有一個該變量霉祸,且在線程內(nèi)部任何地方都可以使用筑累,線程之間互不影響,這樣一來就不存在線程安全問題丝蹭,也不會嚴重影響程序執(zhí)行性能慢宗。
雖然ThreadLocal能夠解決上面說的問題,但是由于在每個線程中都創(chuàng)建了副本奔穿,所以要考慮它對資源的消耗镜沽,比如內(nèi)存的占用會比不使用ThreadLocal要大。
ThreadLocal類
ThreadLocal是如何為每個變量建立副本的贱田。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
獲取當前線程的ThreadLocalMap淘邻,獲取成功則返回value值,失敗則調(diào)用setInitialValue()湘换。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
threadLoacls是一個ThreadLoaclMap類型,他是ThreadLoacl類的內(nèi)部類:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
....
}
Entry類是ThreadLocalMap的內(nèi)部類统阿,且繼承自WeakReference彩倚,并且使用ThreadLoacl類型作為鍵值。
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;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 在每個線程Thread內(nèi)部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals扶平,這個threadLocals就是用來存儲實際的變量副本的帆离,鍵值為當前ThreadLocal變量,value為變量副本(即T類型的變量)
- 在Thread里面结澄,threadLocals為空哥谷,當通過ThreadLocal變量調(diào)用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化麻献,并且以當前ThreadLocal變量為鍵值们妥,以ThreadLocal要保存的副本變量為value,存到threadLocals勉吻。
- 然后在當前線程里面监婶,如果要使用副本變量,就可以通過get方法在threadLocals里面查找齿桃。
通俗說明就是惑惶,每個線程在創(chuàng)建的時候都會創(chuàng)建自己ThreadLocal類型的私有對象,通過這個私有對象作為Thread里的map的key值短纵,可以找出對應(yīng)的value带污,從而達到線程間value復(fù)制且不相互影響的作用。