一.ThreadLocal
Thread中有一个ThreadLocalMap类型的属性,threadLocals:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal的 set() 方法:拿到当前线程的 threadLocals,然后往其中塞值,key是ThreadLocal本身,value是塞入的值。
取出Map的时候,如果为空,则初始化Map。
ThreadLocal的 get() 方法:从线程中拿出threadLocals,如果不为空,从map中拿出键值对。如果Map为空或者没有拿到值,则直接返回null。
二.InheritableThreadLocal
首先看一个例子:
没有使用 InheritableThreadLocal 之前的输出结果:
使用 InheritableThreadLocal 之后的输出结果:
透过现象看本质,分析子线程能够从父线程那里继承的值的原理:
看看 InheritableThreadLocal 的源码:重写了ThreadLocal的3个方法
在Thread中,还有另外一个类型为ThreadLocalMap类型的属性,inheritableThreadLocals。
重写的 getMap() 和 createMap() 方法,把之前对 threadLocals 的操作,换成了对 inheritableThreadLocals 。
get() 方法没有被重写,还是调用的ThreadLocal的get。在get()方法中:
实际调用获取到的是,InheritableThreadLocal重写后的inheritableThreadLocals。
那到底是在何时何地传递过来的呢?
InheritableThreadLocal的childValue方法注释中写道:Computes the child's initial value for this inheritable thread-local variable as a function of the parent's value at the time the child thread is created. This method is called from within the parent thread before the child is started.
大概意思是在子线程被创建的时候,做了从父线程传递值到子线程的操作。下面,看一下线程的创建过程:
跟着进去几层后,发现了使用到inheritableThreadLocals的地方,继续看 ThreadLocal.createInheritedMap:
真相就快浮出水面了,继续看 ThreadLocalMap 的构造方法:
可以看到,首先拿到父线程中的键值对表-table,然后提供for循环,把父线程中的这些值复制到我们新创建的线程的inheritableThreadLocals中。这种模式,有点类似于ClassLoader的 loadClass() 的机制。
总结:新创建的线程中 inheritableThreadLocals 中就已经有了值。