从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。