在java中,如果我们多线程操作变量的时候,需要加上同步控制机制,原因是多线程操作一个变量,那么如果每个线程都操作自己线程的变量,那就不用加锁了,也不用加同步控制了。
ThreadLocal就是这个作用,比如在Web开发中,我们用ThreadLocal来保存用户信息,然后传递后台多个service,然后每个线程单独获取自己的用户信息;
初始化代码也比较简单:
使用比较简单,通过重载initialValue()这个方法进行初始化,或通过set进行设置,然后get使用即可,整个使用过程类似于HashMap。
那如何神奇的控制不同的线程保存不同的数据,从而达到线程的共享那,如下:
首先代码中通过Thread.currentThread()来获取当前的线程id,通过线程id获取对应的ThreadLocalMap,这个getMap,其实是获取Thread的成员变量如下:
此成员变量定义如下:
然后再来看这句话:
即通过本ThreadLocal的对象作为key,获取Entry对象后,再获取它的value,如果为null那,那就调用setInitialValue()进行初始化,代码如下:
此线程的map如果存在,不为null,直接更新,返回默认的初始化值,即initialValue()的返回值,如果不存在,则调用createMap(t,value);来创建map,如下:
说实话代码挺绕的,找了网上一张图,会形成如下的结构:
【图来自互联网,侵权删除】
我们从这个图可以看到,ThreadLocal是所有线程的map的公共key,还要注意到,这个map比较特殊,是内部自己实现的,通过线性探测的方法来解决哈希冲突的,即如果槽位已经被占用了,则通过一个函数计算得到下一个槽位, 这种方法解决冲突的效率比较低,所以不建议用太多的ThreadLocal变量。
Threadlocal相关的数据结构:
【图片来自互联网,侵权删除】
【图片来自互联网,侵权删除】
从上图中可以看到Entry继承自弱应用,下次gc的时候会回收,但是只有key是弱引用,value还是强引用,下次gc的时候,key被回收而value可能一直不会被回收。
所以解决办法是,使用过之后记得通过remove()进行删除。
ThreadLocal适用于无状态的线程内变量共享的场景,比如我们说的通过ThreadLocal保存每个线程特有的信息,比如线程标识(打日志的时候适用,便于排查问题)。
ThreadLocal有一定的内存泄漏分享,记得要remove。