今天听到一个老哥说道ThreadLocal在源码设计上面的一些好处,于是决定把ThreadLocal源码彻底分析一下。
首先,我们来看下set方法
可以看到,这个方法里,先获得了当前线程,之后将当前线程传入了一个方法
此处返回了当前线程的一个成员变量
此时我们第一次调用,肯定为空,那么我们进入为空的判断看下方法
可以看到,new了一个ThreadLocalMap并将当前的ThreadLocal对象作为键,我们要存入的值作为值传入
来看下这个ThreadLocalMap到底为何物吧。
这就是它的庐山真面目,一个ThreadLocal里的内部类,我们看下它的构造方法
创建了一个16长度的Entry数组
之后做了一个二进制与运算,拿到一个整型,并将我们传入的this和值当做参数传入了一个Entry对象并赋值给了数组,请记住这里面做位运算的数是使用了AutomicInteger做++操作的,因为ThreadLocal可以被多个线程转换为索引的
这就是最后的操作,将我们的当前ThreadLocal对象变成了一个弱引用,方便回收,并将我们传入的值赋值给了其内部的变量value.
再来看下我们的get()方法
可以看到,先拿到了我们当前线程,并通过当前线程拿到了当前的ThreadLocalMap,这里不再截图,可以看上面set()方法的讲解
如果map不为空的话就会将我们的this传入getEntity方法()
可以看到,依然是做了与二进制运算拿到保存在Entry数组里对应的Entry.
再次回到此图,可以看到直接返回了我们保存在Entry中value变量的值。
而如果Map为空呢?
点进initialValue()方法
返回了一个Null.
并且因为接下来如果当前线程的ThreadLocalMap不为空则将当前ThreadLocal与null值传入。
如果为空则创建一个。
总结:ThreadLocal实现了多线程间能根据线程来保存值的原因在于它会拿到当前线程里的一个属性,ThreadLocalMap,并且因为是线程里的属性,所以当然是线程安全的。之后创建一个数组,并将创建一个将我们ThreadLocal以及我们要存入的值作为参数传入的Entry对象,并存入数组。而在Entry对象内部则是将我们的this变成了一个弱引用,并将我们的值赋值给了它内部的一个成员变量。
而当我们想要拿到的时候,它则是从当前线程拿到对应的ThreadLocalMap,并通过map将this传入拿到当前线程对应的Entry,之后直接从Entry中取到值
此处可能有点难以理解,但只要记住,我们的ThreadLocal是唯一的,但我们的ThreadLocalMap()是每个线程都不同的,它是线程的一个属性。我们存入的时候,是根据这个ThreadLocal获得索引存入table数组中的。所以我们才能通过固定的ThreadLocal再次拿到索引,从而在不同的map里拿到对应Entry,达到线程隔离的效果。