ThreadLocal实现原理分析
一. ThreadLocal是什么ThreadLocal 可以理解为线程本地变量,每个线程拥有本地变量的副本,各个线程之间的变量互不干扰,主要用于保证多线程环境下变量的安全。 也就是对于同一个 ThreadLocal,每个线程通过 get、set、remove 接口操作只会影响自身线程的数据,不会干扰其他线程中的数据。 二. ThreadLocal的数据结构ThreadLocal 的数据结构如下图所示: 可以看出,每个线程都拥有自己的一个 ThreadLocalMap,它是 ThreadLocal 中的内部类,使用数组来存储数据,数组存放的元素类型为 Entry。Thread 类中包含的成员变量 threadLocals 就是 ThreadLocalMap 类型的,类图如下所示: 可以看出,ThreadLocalMap 没有实现 Map 接口,是用独立的方式实现了 Map 的功能,其内部的 Entry 也是独立实现的,代码如下:
Entry 继承自 WeakReference(弱引用,生命周期只能存活到下次GC前),但只有 Key 是弱引用类型的,Value 并非弱引用。 可以简单理解成 ThreadLocalMap 的 Key 是 ThreadLocal 变量,Value 是线程本地的数据。 三. ThreadLocal核心方法解析ThreadLocal 类提供如下几个核心方法:
get() 方法实现:
set() 方法实现:
remove() 方法实现:
四. 问题分析1.ThreadLocalMap 里 Entry 为何声明为 WeakReference? 因为如果这里使用普通的 key-value 形式来定义存储结构,实质上就会造成节点的生命周期与线程强绑定,只要线程没有销毁,那么节点在GC分析中一直处于可达状态,没办法被回收,而程序本身也无法判断是否可以清理节点。 弱引用是Java中四档引用的第三档,比软引用更加弱一些,如果一个对象没有强引用链可达,那么一般活不过下一次GC。当某个 ThreadLocal 已经没有强引用可达,则随着它被垃圾回收,在 ThreadLocalMap 里对应的 Entry 的键值会失效,这为 ThreadLocalMap 本身的垃圾清理提供了便利。 2.什么情况下会发生内存泄漏? 发生 GC 时弱引用 Key会被回收,而 Value 不会回收,如果创建 ThreadLocal 的线程一直持续运行,并且一直不执行get、set、remove方法,这些 Key 为 null 的 Entry 的 Value 就会一直存在一条强引用链:Thread Ref -> Thread -> ThreadLocalMap -> Entry -> Value,那么这个Entry对象中的 Value 就有可能一直得不到回收,从而发生内存泄漏。 3.如何避免内存泄漏? 既然 Key 是弱引用,那么我们要做的事,就是在调用 ThreadLocal 的 get()、set() 方法完成后及时调用 remove() 方法,将 Entry 节点和 Map 的引用关系移除,这样整个 Entry 对象在 GC Roots 分析后就变成不可达了,下次 GC 的时候就可以被回收了。 (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |