面试常常考的 ThreadLocal 类的使用和解析

什么是 ThreadLocal

ThreadLocal 是一个本地线程副本变量工具类,各个线程都拥有一份线程私有的数据,线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用。

ThreadLocal 提供了线程安全的另一种思路,我们平常说的线程安全主要是保证共享数据的并发访问问题,通过 sychronized 锁或者 CAS 无锁策略来保证数据的一致性。

ThreadLocal 结构

p.jpg

ThreadLocal 的核心机制:

1、每个 Thread 线程内部都有一个 Map。
2、Map 里面存储线程本地对象(key)和线程的变量副本(value)
3、Thread 内部的 Map 是由 ThreadLocal 维护的,由 ThreadLocal 负责向 map 获取和设置线程的变量值。
对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,彼此之间互不干扰。

举例:

p.jpg

下面的例子有 3 个线程 [thread#1],[thread#2],[thread#3] 修改类变量 initValue, 当类变量是 ThreadLocal 的时候 3 个线程修改的值互不影响,打印的结果都是 66

上面的例子 3 个线程是如果做到同时独立修改变量的,答案就在 ThreadLocal 的 set(),get() 方法里面.

下面我们再来看看 ThreadLocal

ThreadLocal 类提供的几个核心方法:

image.png

1、get() 方法用于获取当前线程的副本变量值。
2、set() 方法用于保存当前线程的副本变量值。
3、initialValue() 为当前线程初始副本变量值。
4、remove() 方法移除当前前程的副本变量值。

get() 方法

image.png

1、获取当前线程的 ThreadLocalMap 对象 threadLocals
2、从 map 中获取线程存储的 K-V Entry 节点。
3、从 Entry 节点获取存储的 Value 副本值返回。
4、map 为空的话返回初始值 null,即线程变量副本为 null,需要注意的是在使用中要判断是否为空指针 NullPointerException。

set() 方法

image.png

1、获取当前线程的成员变量 map
2、map 非空,则重新将 ThreadLocal 和新的 value 副本放入到 map 中。
3、map 空,则对线程的成员变量 ThreadLocalMap 进行初始化创建,并将 ThreadLocal 和 value 副本放入 map 中。

remove() 方法

image.png

Thread 线程内部的 Map 在类中描述如下:
image.png

可以看到,这个 ThreadLocalMap 是线程中的变量,也就是说每个线程都是相互独立的

image.png

应用场景
类似单例类 TransactionSynchronizationManager,

RequestContextHolder 中就是通过 ThreadLocal 保存各自线程变量的副本,这样就不需要重新创建类。

一个知识点延伸出这么多知识点,关于弱引用、 内存优化等,不仅能考验求职者的对该知识点的掌握程度,又能考验求职者的知识面,难怪阿里百度这样的大公司喜欢在面试时拿它来考验求职者。

  
    展开阅读全文