什么是ThreadLocal
ThreadLocal 提供了线程内部的局部变量,当在多线程环境中使用 ThreadLocal 维护变量时,会为每个线程生成该变量的副本,每个线程只操作自己线程中的变量副本,不同线程间的数据相互隔离、互不影响,从而保证了线程的安全。
ThreadLocal 适用于无状态,副本变量独立后不影响业务逻辑的高并发场景,如果业务逻辑强依赖于变量副本,则不适合用 ThreadLocal 解决,需要另寻解决方案。
ThreadLocal 的数据结构
在 JDK8 中,每个线程 Thread 内部都维护了一个 ThreadLocalMap 的数据结构,ThreadLocalMap 中有一个由内部类 Entry 组成的 table 数组,Entry 的 key 就是线程的本地化对象 ThreadLocal,而 value 则存放了当前线程所操作的变量副本。每个 ThreadLocal 只能保存一个副本 value,并且各个线程的数据互不干扰,如果想要一个线程保存多个副本变量,就需要创建多个ThreadLocal。
一个 ThreadLocal 的值,会根据线程的不同,分散在 N 个线程中,所以获取 ThreadLocal 的 value,有两个步骤:
- 第一步,根据线程获取 ThreadLocalMap
- 第二步,根据自身从 ThreadLocalMap 中获取值,所以它的 this 就是 Map 的 Key
当执行 set() 方法时,其值是保存在当前线程的 ThreadLocal 变量副本中,当执行get() 方法中,是从当前线程的 ThreadLocal 的变量副本获取。所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了线程的隔离,互不干扰。
ThreadLocal 的核心方法
ThreadLocal 对外暴露的方法有4个:
- initialValue()方法:返回为当前线程初始副本变量值。
- get()方法:获取当前线程的副本变量值。
- set()方法:保存当前线程的副本变量值。
- remove()方法:移除当前前程的副本变量值
ThreadLocal 的哈希冲突的解决方法:线性探测
和 HashMap 不同,ThreadLocalMap 结构中没有 next 引用,也就是说 ThreadLocalMap 中解决哈希冲突的方式并非链表的方式,而是采用线性探测的方式,当发生哈希冲突时就将步长加1或减1,寻找下一个相邻的位置。如下图所示:
流程说明:
- 根据 ThreadLocal 对象的 hash 值,定位到 table 中的位置 i;
- 如果当前位置是 null,就初始化一个 Entry 对象放在位置 i 上;
- 如果位置 i 已经有 Entry 对象了,如