ThreadLocal
ThreadLocal 的作用主要是做数据隔离。
ThreadLocal 类提供线程局部变量。这些变量与正常的变量不同,每个线程访问一个 (通过它的get或set方法)都有它自己的、独立初始化的变量副本.
ThreadLocalMap 是一个定制的哈希映射,仅适用于维护线程本地值。ThreadLocalMap 类是包私有的,允许在Thread类中声明字段。为了帮助处理非常大且长时间的使用,哈希表 entry使用了对键的弱引用。有助于GC回收。
//创建一个ThreadLocal变量
static ThreadLocal<String> localVariable = new ThreadLocal<>();
为什么下需要 ThreadLocal 呢?
并发场景下,会存在多个线程同时修改一个共享变量的场景。这就可能会出现线程安全问题。
synchronized
和 Lock
可以解决线程安全问题,但是可能会导致系统变慢。
使用 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,即还没有初始化,走初始化方法。