zoukankan      html  css  js  c++  java
  • 源码阅读笔记 之 ThreadLocal —— 不复杂,却有点绕的一个 per thread API

    (已于20201014重新更新)

    ThreadLocal 源码阅读笔记:

    1. 初探 ThreadLocal:

    从类层面上看,ThreadLocal 中定义了 ThreadLocalMap。操作这个map需要通过 ThreadLocal 提供的 public API,常用的API有 get(), set(T) 等。

    从对象层面上看,Thread 类里面封装了一个类型为 ThreadLocal.ThreadLocalMap、名称为 threadLocals 的实例属性。

    从代码运行层面上看,操作的类型顺序为 ThreadLocal --> Thread --> ThreadLocalMap --> ThreadLocal --> <和ThreadLocal对象绑定的 Object>。第一次访问 ThreadLocal 是因为操作 ThreadLocalMap 的 API 封装在 ThreadLocal 中,而这个map是Thread对象的某个属性,需要通过Thread对象访问;访问到map后,仍需要ThreadLocal作为map的key来查找当前Thread所使用的某个ThreadLocal对象绑定的 Object。

    你没看错,是“某个ThreadLocal对象”。这意味着,Thread可以拥有多个ThreadLocal,即在代码中让Thread操作多个ThreadLocal对象(或者说拥有它们的访问权),这些对象在 ThreadLocalMap 中遵从哈希表存放 key-val 对的基本规则。下面讲 ThreadLocalMap 中如何解决哈希碰撞。

    2. ThreadLocal 中 ThreadLocalMap 的 hash碰撞解决策略:线性探测法。

    前面说到“ThreadLocalMap 中遵从哈希表存放 key-val 对的基本规则”,因此当你的 Thread 使用了多于一个 ThreadLocal 对象时,就会在 ThreadLocalMap 内部的 table 中产生 hash碰撞的可能。

    如果你的代码从头到尾只让 Thread 使用一个 ThreadLocal 对象,那么不好意思,ThreadLocal 的 hash碰撞跟你一毛钱关系没有。

    Note:你也可以让多个 Thread 共用一个 ThreadLocal 对象,这并没有任何不妥。因为 ThreadLocal 只是 ThreadLocalMap 中的 key,key 往往是可重复的,这可以类比为标签,比如每个人都应该有一个“姓名”,但是这个“姓名”下面的真实属性(val)却不尽相同,很容易在人群中找到“姓名”不同的两个人。当然,如果你是在哈希表中存放所有用户的信息,那么key应当是每个用户信息中具有唯一性的那个属性。

    ThreadLocal 类中封装了一个常量实例域:threadLocalHashCode,它通过一个静态方法 nextHashCade(); 来生成。通过这个静态方法,为不同的 ThreadLocal 对象派发一个 hashcode,这就是这个静态方法的作用。在 Java HashMap 中我们知道程序会对放入map中的 key 做一个 hash运算,这个 nextHashCade() 方法的作用是一样的。

    Note:第一个 threadLocalHashCode 是 0,下一个是 0+0x61c88647,即步进 0x61c88647。

    如果你的某一个线程池代码需要处理线程副本变量,并且使用到了多个 ThreadLocal 对象,当这些对象很多时,nextHashCade() 方法分发的 hashcode “碰巧”发生了碰撞,那么碰撞之后的下标寻找将在当前下标的基础上+1 去寻找新下标,即 线性探测法。而 HashMap 中的冲突解决策略是链表法,即用一个 Node.next 指针将各个元素串联到一个链表上。

    3. ThreadLocal 的内存泄漏:

    ThreadLocalMap 中的 Entry 是继承了 WeakReference 的,但只有 Entry 中的 kye 对象即 ThreadLocal 对象会被 WeakReference 关联上,Entry 中的 value 是没有被弱引用关联的。这就导致当 ThreadLocal 不再被线程引用之后,垃圾回收器对 Thread 中的 ThreadLocalMap 进行回收时可以回收掉 ThreadLocal,这样就会在 ThreadLocalMap 中产生 Entry 的 key 为 null 的 Entry。而 value 还被这个 ThreadLocalMap 的一些 Entry 关联着。就产生了所谓的“内存泄漏”。

    因此 ThreadLocal 提供了 remove() 方法来释放这些 value,查看源码可以发现它调用 ThreadLocalMap 的 remove(ThreadLocal) 方法,清理所有 key == null 的 entry,而不是只清理一个。

  • 相关阅读:
    机器学习算法(SVM)公开课4月25日开讲
    手把手教你做文本挖掘
    ActiveReports公开课开启报名,学习如何解决中国式复杂报表难题
    DevExpress免费公开课,讲解即将发布的16.2新版功能
    Stimulsoft入门视频
    免费公开课,讲解强大的文档集成组件Aspose,现在可报名
    中国式商业智能报表ActiveReports免费公开课,10月20日开讲
    JavaScript图表FusionCharts免费在线公开课,由印度原厂技术工程师主讲,10月13日发车
    LoadRunner免费公开课,惠普金牌讲师亲授
    DevExpress VCL v16.1.3发布
  • 原文地址:https://www.cnblogs.com/christmad/p/11879681.html
Copyright © 2011-2022 走看看