赞
踩
我们写一个测试代码:
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。
看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值。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。