從demo入手看效果
代碼Demo
static ThreadLocal tl1 = new ThreadLocal();static ThreadLocal tl2 = new ThreadLocal();static ThreadLocal tl3 = new ThreadLocal();public static void main(String[] args) {tl1.set("123");tl2.set("456");tl3.set("4586");Thread t1 = new Thread(() -> {System.out.println("t1:tl1-before:" + tl1.get());System.out.println("t1:tl2-before:" + tl2.get());System.out.println("t1:tl3-before:" + tl3.get());ThreadLocal tl4 = new ThreadLocal();tl1.set("1231");tl2.set("4561");tl3.set("12312");tl4.set("123121231212312");System.out.println("t1:tl1-" + tl1.get());System.out.println("t1:tl2-" + tl2.get());System.out.println("t1:tl3-" + tl3.get());System.out.println("t1:tl4-" + tl4.get());});t1.start();Thread.sleep(5000);System.out.println("main:tl1-" + tl1.get());System.out.println("main:tl2-" + tl2.get());System.out.println("main:tl3-" + tl3.get());
執行結果:
t1:tl1-before:null
t1:tl2-before:null
t1:tl3-before:null
t1:tl1-1231
t1:tl2-4561
t1:tl3-12312
t1:tl4-123121231212312
main:tl1-123
main:tl2-456
main:tl3-4586
結論:
根據結果可以發現 相同的ThreadLocal 在main線程和t1 線程存入不同的數據,拿到的結果不一樣,即使主線程先在ThreadLocal執行set方法,在t1線程中還是獲取為空,可以說明在T1線程內部獲取ThreadLocal是獲取不到main線程的,因此可以證明實現了線程隔離。
源碼分析
Thread:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal類中包含一個靜態類 ThreadLocalMap,是一個定制化的哈希表,用于存儲線程的私有數據。鍵為ThreadLocal對象(弱引用),值為線程的局部變量(強引用)
ThreadLocal的set方法源碼:
設置該線程局部變量的當前線程副本到指定的值。大多數子類都不需要這樣做重寫此方法,僅依賴于{@link #initialValue}方法設置線程局部變量的值。public void set(T value) {Thread t = Thread.currentThread();//ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}}
在上面set方法里先獲取到執行該方法的線程對象,再基于此對象去獲取該線程的ThreadLocalMap 來實現線程間的隔離數據
getMap
獲取與ThreadLocal相關聯的映射
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
createMap:創建該線程的ThreadLocalMap
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
new ThreadLocalMap(this, firstValue)
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}
**set方法執行結論:**先獲取到執行該方法的線程對象,然后基于這個對象獲取ThreadLocalMap,進行實例化,賦值。賦值的key就是ThreadLocal,雖然ThreadLocal在多線程中復用,但是實際存儲數據的是ThreadLocalMap,這個ThreadLocalMap是和Thread綁定的
get方法
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();}
getMap
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
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);}if (this instanceof TerminatingThreadLocal) {TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);}return value;}
get方法執行結論: 先拿到執行方法的當前線程 根據當前線程去獲取map,有就以ThreadLocal為key去獲取value。