多个线程修改ThreadLocal里面的值,会是什么情况?
我们写一个测试代码:
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocal<String> sharedName = new ThreadLocal<>();
ThreadLocal<Integer> sharedId = new ThreadLocal<>();
//main线程最先修改
System.out.println(Thread.currentThread().getName() + "初始值:" + sharedName.get()+" "+sharedId.get());
sharedName.set("main");
sharedId.set(103);
System.out.println(Thread.currentThread().getName() + "设定后:" + sharedName.get()+" "+sharedId.get()+"\n");
Thread tA = new Thread() {
@Override
public void run() {
System.out.println(this.getName() + "开始值:" + sharedName.get()+" "+sharedId.get());
sharedName.set("线程A");
sharedId.set(101);
sleepTime(100);
System.out.println(this.getName() + "设定后:" + sharedName.get()+" "+sharedId.get()+"\n");
}
};
Thread tB = new Thread() {
@Override
public void run() {
sleepTime(100);
System.out.println(this.getName() + "开始值:" + sharedName.get()+" "+sharedId.get());
sharedName.set("线程B");
sharedId.set(102);
System.out.println(this.getName() + "设定后:" + sharedName.get()+" "+sharedId.get()+"\n");
}
};
tA.start();
tB.start();
}
private static void sleepTime(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
main初始值:null null
main设定后:main 103
Thread-0开始值:null null
Thread-0设定后:线程A 101
Thread-1开始值:null null
Thread-1设定后:线程B 102
程序有三个线程main,tA,tB,三个线程都修改了sharedName和sharedId(后面简称name和id),但是main线程设置的name和id,tA和tB并没有读取到,ta和tB一开始读取的是null。
为什么tA线程读不到main线程设置的name?
看ThreadLoca.set()方法:
//ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap是ThreadLocal的静态内部类
//ThreadLocalMap的构造器
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方法,通过getMap()
获取当前线程的ThreadLocalMap, 然后把设置的字符串“main”,以<key,value>的形式存放在ThreadLocalMap里面。看下面Thread的源码可知,每个Thread都有一个ThreadLocalMap类型的成员变量。
public
class Thread implements Runnable {
...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
所以,保存的时候,保存在当前线程的ThreadLocalMap里面。那读取的时候又是怎么读的?
看看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();
}
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;
}
protected T initialValue() {
return null;
}
可以看出,get()方法在读取的时候,也是读的当前线程的ThreadLocalMap。所以tA线程读取的name是tA线程自己的ThreadLocalMap,一开始map为空,所以name是null,后来设置值了,就读到了自己设置的值。
明明是同一个ThreadLocal对象,为啥获取的是不同name值了?
这就好比,两个不同的HashMap,mapA 和mapB , 它俩都有一组key值相同的Entry,但是value值不一样,所以读取的value也不一样。下图一目了然,tl是ThreadLocal的缩写。
结论:tA线程读取name,是由ThreadLocal控制的,ThreadLocal获取的是tA线程自己的ThreadLocalMap,所以tA线程读取的也是自己的ThreadLocalMap的name值。读不到main线程设置的name值。