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