1 前言
此类提供线程本地变量,与普通变量不同,因为每个访问一个变量(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。
ThreadLocal 实例通常是期望将状态与线程(例如,用户ID或事务ID)关联的类中的 private static 字段。
例如,下面的类生成每个线程本地的唯一标识符。线程的ID是在第一次调用ThreadId.get() 时赋值的,并且在以后的调用中保持不变。
只要线程是活跃的并且 ThreadLocal 实例是可访问的,则每个线程都对其线程本地变量的副本持有隐式的引用。线程消失后,线程本地实例的所有副本都会被 GC(除非存在对这些副本的其他引用)。
2 继续体系
继承?不存在的,这其实也是 java.lang 包下的工具类,但是 ThreadLocal 定义带有泛型,说明可以储存任意格式的数据。
3 属性
ThreadLocal 依赖于附加到每个线程(Thread.threadLocals和InheritableThreadLocals)的线程线性探测哈希表。
threadLocalHashCode
ThreadLocal 对象充当key,通过 threadLocalHashCode 进行搜索。这是一个自定义哈希码(仅在ThreadLocalMaps 中有用),它消除了在相同线程使用连续构造的threadlocal的常见情况下的冲突,而在不太常见的情况下仍然表现良好。
ThreadLocal 通过这样的 hashCode,计算当前 ThreadLocal 在 ThreadLocalMap 中的索引
连续生成的哈希码之间的差值,该值的设定参考文章ThreadLocal的hash算法(关于 0x61c88647)
注意 static 修饰。ThreadLocalMap 会被 set 多个 ThreadLocal ,而多个 ThreadLocal 就根据 threadLocalHashCode 区分
4 ThreadLocalMap
自定义的哈希表,仅适用于维护线程本地的值。没有操作导出到ThreadLocal类之外。
该类包私有,允许在 Thread 类中的字段声明。为帮助处理非常长的使用寿命,哈希表节点使用 WeakReferences 作为key。
但由于不使用引用队列,因此仅在表空间不足时,才保证删除过时的节点。
static class ThreadLocalMap { /** * 此哈希表中的节点使用其主引用字段作为key(始终是一个 ThreadLocal 对象),继承了 WeakReference。 * 空键(即entry.get()== null)意味着不再引用该键,因此可以从表中删除该节点。 * 在下面的代码中,此类节点称为 "stale entries" */ static class Entry extends WeakReference<ThreadLocal<?>> { /** 与此 ThreadLocal 关联的值 */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int size = 0; private int threshold; // 默认为 0
特点
- key 是 ThreadLocal 的引用
- value 是 ThreadLocal 保存的值
- 数组的数据结构
5 set
5.1 ThreadLocal#set
将此线程本地变量的当前线程副本设置为指定值。子类无需重写此方法,而仅依靠initialValue方法设置线程本地变量的值。
执行流程
- 获取当前线程
- 获取线程所对应的ThreadLocalMap。每个线程都是独立的,所以该方法天然线程安全
- 判断 map 是否为 null
- 否,K.V 对赋值,k 为this(即当前的 ThreaLocal 对象)
- 是,初始化一个 ThreadLocalMap 来维护 K.V 对
来具体看看ThreadLocalMap中的 set