关于ThreadLocal大家肯定都知道,网上的文章也很多。但由于我没有仔细看过源码,仅靠零散获取的知识,一直以来始终有一个问题困扰着我:
ThreadLocal中有一个静态内部类ThreadLocalMap,其核心是内部维护着的一个Entry[]数组。同时,每个Thread实例各自持有一个ThreadLocalMap对象。每当往ThreadLocal里set值的时候,就是以当前线程为key,期望的值为value,放到各自的ThreadLocalMap实例中的Entry数组里去。那么问题是,为什么需要一个Entry数组,因为key总是这个当前线程,其他线程也无法放到这个数组里中啊?换句话说,这个数组应该永远只有一组键值对。
但事实很明显不是这样的,问题出在哪儿了呢。
有时面试时,不少人会被和我一起掉到坑里,“对哦,为什么呢”?
有人回答说,是不是因为ThreadLocalMap是静态内部类,只实例化一次,共享变量之类的,或者说Entry里的key是弱引用,如此云云。其实,和这些都无关。
为此我还写了个demo证明和静态内部类无关:
后来我仔细看了一边代码,发现了问题。原来我误认为了Entry[]数组里的key是Thread,其实是ThreadLocal。借助 https://www.jianshu.com/p/c1e58529c2e5 的图片和介绍,简单说明一下。
Thread实例和ThreadLocalMap实例是一对一的关系,每一个Thread实例都拥有一个自己的ThreadLocalMap实例。而ThreadLocalMap实例,实际上可以看作是一个Entry数组,也就是说,每当我们需要一个线程专属变量时,就在这个线程所持有的ThreadLocalMap上新增一个entry键值对,以对应的 ThreadLocal弱引用 为key,以线程专属变量为value。
而ThreadLocal实例与前两者的关系是可以多对多的关系.ThreadLocal的get()、set()方法是以自身为key,往各个thread实例中的threadLocalMap实例中塞进entry,以此来完成向thread中注入线程专属变量.而且,当我们调用threadLocal实例时,我们能轻而易举地获得当前线程(通过Thread.currentThread()方法),然后在以自身(this)作为key,这样便能在同一个线程中复用一个变量。