深入学习java ThreadLocal的源码知识

  

<强>
  

  

ThreadLocal是每个线程自己维护的一个存储对象的数据结构,线程间互不影响实现线程封闭。一般我们通过ThreadLocal对象的获?设置方法存取对象。

  


  

  

ThreadLocal的设置方法源码如下

        公共空集(T值){   线程t=Thread.currentThread ();   ThreadLocalMap地图=getMap (t);//根据当前线程获得ThreadLocalMap对象   如果(地图!=null)   地图。集(这个值);//如果有则集   其他的   createMap (t值);//否则创建ThreadLocalMap对象   }   ThreadLocalMap getMap(线程t) {   返回t.threadLocals;   }   空白createMap(线程t, t firstValue) {   t。threadlocal=new ThreadLocalMap(这个,firstValue);   }      

通过getMap方法,可见我们返回的地图实际上是线程对象的threadlocal属性。而这个ThreadLocalMap就是用来存储数据的结构。
  

  

<强> ThreadLocalMap介绍
  

  

ThreadLocalMap是ThreadLocal的核心,定义在ThreadLocal类里的内部类,他维护了一个Enrty数组.ThreadLocal存/取数据都是通过操作Enrty数组来实现的。
  

  

Enrty数组作为一个哈希表,将对象通过开放地址方法散列到这个数组中。作为对比,HashMap则是通过链表法将对象散列到数组中。
  

  

开放地址法就是元素散列到数组中的位置如果有冲突,再以某种规则在数组中找到下一个可以散列的位置,而在ThreadLocalMap中则是使用线性探测的方式向后依次查找可以散列的位置。
  

  

<强>埃内里介绍
  

  

埃内里在这里我们称之为元素,是散列表中维护的对象单元。
  

     //哈希映射表中的元素使用其引用字段作为键(它始终是ThreadLocal对象)继承WeakReference引用。//注意,零键(即条目。get ()==null)表示不再引用该键,因此可以从表中删除该元素。//这些元素在下面的代码中称为“旧元素”。//这些“旧元素”就是脏对象,因为存在引用不会被GC,//为避免内存泄露需要代码里清理,将引用置为空,那么这些对象之后就会被GC清理。//实际上后面的代码很大程度上都是在描述如何清理“旧元素”的引用   静态类条目延伸WeakReference      

到这里可能有两个疑问
  

  

1,既然要存储的内容是线程独有的对象,为什么不直接在线程里设置一个属性直接存储该对象?或者说为什么要维护一个条目散列表来存储内容并以ThreadLocal对象作为钥匙吗?
  

  

答:一个ThreadLocal对象只属于一个线程,但一个线程可以实例化ThreadLocal对象。而ThreadLocalMap维护的数组存储的就是以ThreadLocal实例作为关键的入口对象。
  

  

2, ThreadLocalMap中的埃内里为什么要继承weakreference # 63;
  

  

答:首先弱引用会在ThreadLocal对象不存在强引用的情况,弱引用对象会在下次GC时被清除。
  将ThreadLocal对象作为弱引用目的是为了防止内存泄露。
  

  

假设埃内里的关键不是弱引用,即使在我们的代码里threadLocal引用已失效,threadLocal也不会被GC,因为当前线程持有ThreadLocalMap的引用,而ThreadLocalMap持有条目数组的引用,入口对象的关键又持有threadLocal的引,用threadLocal对象针对当前线程可达,所以不会被GC。
  

  

而埃内里的关键值threadLocal作为弱引用,在引用失效时会被GC。但即使threadLocal做为弱引用被GC清理,条目[]还是存在条目对象,只是关键为null, vlue对象也还存在,这些都是脏对象。弱引用不单是清理了threadLocal对象,它的另一层含义是可以标识出埃内里[]数组中哪些元素应该被GC(我们这里称为旧元素),然后程序里找出这些条目并清理。
  

  

<强> ThreadLocalMap组的方法
  

  

回到前面提到组的方法,当地图不为空时会调用ThreadLocalMap组的方法。
  

  

ThreadLocalMap组的方法描述了如何将值散列到哈希表中,是开放地址法以线性探测方式散列的实现。在成功集值之后,尝试清理一些旧元素,如果没有发现旧元素则判断阈值,确认哈希表是否足够大,是否需要扩容。如果哈希表过于拥挤,获?设置值会发生频繁的冲突,这是不期望的情况.ThreadLocalMap组的方法代码及详细注释如下

深入学习java ThreadLocal的源码知识