有關Java線程隔離方面的問題也是面試中必問的一個問題,今天就好好看一下ThreadLocal 實現原理,也是我們在解決多線程數據安全時比較常用的一個手段
實現理論
將變量保存爲每個線程棧內部的變量,因爲線程棧與線程棧之間內部的變量不不會相互影響的,所以就不會存在數據不安全的情況。
代碼實現
Thread類
ThreadLocal.ThreadLocalMap threadLocals = null;
Thread內由此Map 負責維護 Thread 內被隔離的變量
ThreadLocalMap
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
此Map中的key爲 ThreadLocal對象,value爲存在Thread被隔離變量的值。
ThreadLocal
當使用ThreadLocal set值的時候
public void set(T value) {
//獲取當前操作線程對象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//將值 放置在 當前線程局部變量 ThreadLocalMap 中
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
//獲取當前線程局部變量 ThreadLocalMap
return t.threadLocals;
}
至此可以發現達到了 線程隔離的效果
當使用 ThreadLocal get值的時候
public T get() {
//獲取當前線程對象
Thread t = Thread.currentThread();
//獲取當前線程局部變量 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//從Map中 根據 threadlocal key及this 獲取value
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
小結
至此大概可以總結出Thread ThreadLocal ThreadLocalMap 之間的關係
Thread 中存在 ThreadLocalMap 的強引用,ThreadLocalMap 中 Entry 存在 key 爲 ThreadLocal 的弱引用,以及任意類型 value 的強引用
此處就會出現 內存泄漏 的情況,當一次GC過後,ThreadLocal 會被回收掉變爲null,所以ThreadLocalMap 中的key變爲null,此時value理應被回收掉,但是如果線程對象沒有結束的話,就會存在 Thread -> ThreadLocalMap -> Entry -> (null,value) 的引用鏈,導致value不會被回收,爲了應對這種情況的發生,ThreadLocal 在get 和 set 操作時會將key爲null的 Entry 的 value 也變爲 null
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// Rehash until we encounter null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
//如果key爲null,則將對應的value也變爲null,同時將長度減一
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
//省略部分代碼
}
}
return i;
}
總結
- Thread -> ThreadLocal.ThreadLocalMap -> Entry -> (weakReferencr<ThreadLocal>,T value)
- 在 ThreadLocalMap set ,get 時 會執行 key爲null 的 Entry 的 value 也變爲null ,同時減少Map長度。