跳到主要内容

ThreadLocal

ThreadLocal 的作用主要是做数据隔离

ThreadLocal 类提供线程局部变量。这些变量与正常的变量不同,每个线程访问一个 (通过它的get或set方法)都有它自己的、独立初始化的变量副本.

ThreadLocalMap 是一个定制的哈希映射,仅适用于维护线程本地值。ThreadLocalMap 类是包私有的,允许在Thread类中声明字段。为了帮助处理非常大且长时间的使用,哈希表 entry使用了对键的弱引用。有助于GC回收。

//创建一个ThreadLocal变量
static ThreadLocal<String> localVariable = new ThreadLocal<>();

为什么下需要 ThreadLocal 呢?

并发场景下,会存在多个线程同时修改一个共享变量的场景。这就可能会出现线程安全问题

synchronizedLock 可以解决线程安全问题,但是可能会导致系统变慢。

使用 ThreadLocal 类访问共享变量时,会在每个线程本地,都保存一份共享变量的拷贝副本。 多线程对共享变量修改时,实际上操作的是这个变量副本,从而保证线程安全。

ThreadLocal 的实现原理

  • Thread 线程类有一个类型位 ThreadLocal.ThreadLocalMap 的实例变量,threadLocals, 即每个线程都有一个属于自己的 ThreadLocalMap。

  • ThreadLocalMap 内部维护着 Entry 数组,每个 Entry 代表一个完整的对象,key 是 ThreadLocal 本身,value 是 ThreadLocal 的泛型值。

  • 并发多线程场景下,每个线程 Thread ,在往 ThreadLocal 里设置值的时候,都是往自己的 ThreadLocalMap 里存,读也是以某个 ThreadLocal 作为引用,在自己的 map 里找对应 的 key,从而实现了线程隔离

  • ThreadLocalMap 的 key 是弱引用(WeakReference)

源码分析

散列算法

  • hashcode 每次自增 java.lang.ThreadLocal#HASH_INCREMENT
  • 元素散列位置(数组下标) = hashcode & (length-1)

set

  • 根据 hashcode 和数组长度求元素放置的位置,即数组下标
  • 从第一步得出的下标开始往后遍历,如果 key 相等,则覆盖 value,如果 key 位 null, 用 新 key、value 覆盖,同时清理历史 key=null 的陈旧数据
  • 如果超过阈值,就需要 rehash
    • 清理一边旧数据
    • 数据长度 >= 阈值的 3/4 (阈值位数组长度的 2/3),就执行扩容操作,把 table 扩容 2 倍
    • 把老数据重新哈希散列进新 table

get

  • 从当前线程中获取 ThreadLocalMap,查询当前 ThreadLocal 变量实例对应的 Entry,如果不为 null, 获取 value,返回。
  • 如果 map 为 null,即还没有初始化,走初始化方法。

remove

  • 将弱引用置空。

ThreadLocal 为什么会导致内存泄露呢?

弱引用

ThreadLocal 的应用场景

SimpleDateFormat