前言:在一次面试过程中被问到ThreadLocal,大家都知道ThreadLocal可以为每个线程单独提供一个副本,从而实现变量间的隔离。在ThreadLocal中set和get操作的key是什么,ThreadLocal又是怎样实现各线程间互不干扰的,本文通过调试ThreadLocal的源码来阐述这些问题。
注:jdk版本:jdk1.7.0_51
1.set与get源码
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); 6 else 7 createMap(t, value); 8 }
1 public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) 7 return (T)e.value; 8 } 9 return setInitialValue(); 10 }
下面通过对源码的调试说明具体流程。
2.具体调试过程
1 public class CodeTest02 2 { 3 public static void main(String[] args) throws InterruptedException 4 { 5 final ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); 6 7 Thread t1 = new Thread(new Runnable() 8 { 9 @Override 10 public void run() 11 { 12 threadLocal.set(1); 13 14 System.out.println(threadLocal.get()); 15 } 16 },"t1"); 17 t1.start(); 18 19 t1.join(); 20 threadLocal.set(2); 21 22 System.out.println(threadLocal.get()); 23 24 25 } 26 27 }
说明:在主线程和t1线程中进行set操作,最后输出如下:
从输出结果可以看出两个线程间的值互不影响。
具体调试过程:
注意:当前threadLocal的地址值为ThreadLocal@430。进入断点,如下图所示。
注意:
1)this的值为当前ThreadLocal对象的值(ThreadLocal@430)。
2)t表示当前线程t1。
3)map为空,注意这里的map为ThreadLocalMap。
转入createMap函数,传入的值为当前线程t1和value(value=1)。
说明:
1)createMap函数会为t1线程创建一个ThreadLocalMap。
从注释中可以看出,ThreadLocal为线程的附属值,所以ThreadLocalMap也为线程的一个附属属性,它被ThreadLocal维护。
2)该ThreadLocalMap的键为this,从调试信息中可以看出this的值为ThreadLocal@430。
接下来看get函数:
说明:
1)通过t1线程,取出ThreadLocalMap,这里获取set中根据t1创建的threadLocals对象。
1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; 3 }
2)在ThreadLocalMap中根据this,也就是当前ThreadLocal@430,取得设置的值。
总结:
1)ThreadLocal中在set操作时,key为当前ThreadLocal对象。
2)ThreadLocal会为每个线程都创建一个ThreadLocalMap,对应程序中的t.threadLocals = new ThreadLocalMap(this, firstValue),ThreadLocalMap为当前线程的属性。
3)通过对每个线程创建一个ThreadLocalMap实现本地副本。当取值时,实际上就是通过key在map中取值,当然此时的key为ThreadLocal对象,而map为每个线程独有的map,从而实现变量的互不干扰。
by Shawn Chen,2018.6.2日,下午。