您的位置:首页 > 其它

(一)ThreadLocal源码分析

2019-04-10 14:32 134 查看
  • ThreadLocal概要
    在我看来ThreadLocal完全可以把他当做一个工具类来看待,因为在ThreadLocal内部根本没有自己的成员变量来控制一些ThreadLocal自己的数据(有个threadLocalHashCode常量,后面会讲述),而不管是我们常用的set,get,remove方法,操作的都是当前线程对象的成员变量,如下:

  • 分析ThreadLocal类

    首先是获取当前线程对象,然后再去获取当前线程对象的ThreadLocalMap成员变量,我们来看看
    getMap(t)这个方法

    我们可以看到直接获取的就是当前线程的一个成员变量:threadLocals


    我们看到set,remove方法也是一样,获取到当前线程对象,然后使用线程的threadLocals属性进行操作,还有一个问题就是在我们new Thread()的时候,这个threadLocals是null,那么我们什么时候为线程对象的这个属性赋值的呢?

    在get方法中会调用这个初始化方法进行创建赋值,因为用户第一次没有设置值得情况下,这个trreadLocals成员变量是没有初始化的,必然map==null,然后去调用CreateMap()方法进行创建

    在createMap()方法中会传入两个参数,其中一个就是当前线程对象,然后方法中就是创建一个
    ThreadLocalMap对象赋值给线程的threadLocals属性.
    总结:我们看到,我们在对ThreadLocal对象做set,get,remove操作的时候,其实背后对应的是
    对当前线程对象的threadLocals属性(即ThreadLocalMap类型的对象)进行set,get,remove操作,下面我们来分析下ThreadLocalMap

  • 分析ThreadLocal$ThreadLocalMap
    在ThreadLocalMap中,会对当前线程的相对应属性(treadLocals)值进行增删改查操作,那么必然去操作某种数据结构,那么我们就需要看看ThreadLocalMap的数据结构了

    我们可以看到ThreadLocalMap中维护的数据结构实际上是个Entry类类型的数组,Entry类型的对象
    中保存两个属性值,一个是我们创建的ThreadLocal对象,一个是我们将要存储的值;我想说到这里我就可能需要解释下了,其实也就是说在一个线程中是可以维护多个ThreadLocal,在一个ThreadLocal中每个线程只可以存储一个Object,然后将这个ThreadLocal对象和要存储的Object封装为一个Entry对象存储在当前线程的threadLocals(ThreadLocalMap类型)属性中。

    这个set方法就是我们在实际开发中使用ThreadLocal.set(object)做的最终操作,先根据ThreadLocal里面的threadLocalHashCode这个常量属性去定位当前ThreadLocal对象在当前线程的ThreadLocalMap对象的Entry[] tab中哪个Entry对象,找到之后如果已经之前做过set操作那么直接覆盖原来值即可,如果这个下标位置的值为null,那么久封装一个Entry对象放入其中。最后数组实际存储值的长度+1,最后还会去判断当前实际存储Entry对象的size是不是大于数组总长度的2/3(len*2/3),如果大于,那么将会进行数组扩容。


    其实remove,get方法都差不多,也是先根据ThreadLocal中的threadLocalHashCode属性来定位在ThreadLocalMap中的Entry[] 中哪个Entry,然后取出这个Entry进行操作

  • 说说怎么定位到对应的Entry
    在我没有去自己阅读源码之前,看一些blog,总是会看到ThreadLocal中的数据结构是个Map,其实不是,而是采用了Map的实现原理来达到Map效果,底层采用的是数组,并没有直接采用Map;我之前还看到过说是通过ThreadLocal来映射对应的ThreadLocalMap,其实在同一个线程中所有的ThreadLocal都映射到同一个ThreadLocalMap,只是不同的ThreadLocal对象映射的ThreadLocalMap中的Entry不一样。


    我们可以看到threadLocalHashCode这个属性值每次在创建初始化时会调用nextHashCode方法,在这个方法中每次都是在上一个HashCode基础上增加一个增量值,然后赋值,所有我们可以知道每个ThreadLocal对象的这个属性值都是不一样的

    我们现在就应该知道是怎么通过当前ThreadLocal对象去映射对应的Entry了。其实就是使用threadLocalHashCode属性值去和(len-1)做个与运算,因为threadLocalHashCode这个属性值在当前ThreadLocal对象中是不可变的,在set操作的时候,使用这个值做与运算找到Entry[ ]下标,自然在get操作的时候做相同的运算,也能找到当前ThreadLocal对应的Entry对象

总结:一个线程可以同时维护多个ThreadLocal,同时也就是说一个线程中可以维护多个副本变量;这个副本变量都是存储在当前线程的threadLocals这个变量中;多个线程操作同一个ThreadLocal对象在存储值的时候,每个线程存储的Entry[] 下标是一样的(key.threadLocalHashCode & (len-1)固定)。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: