唠点面试官爱听的系列之ThreadLocal

开发 前端
ThreadLocal 是 Java 中的一个类,用于创建线程局部变量。每个线程对 ThreadLocal 变量的访问都是独立的,每个线程都会拥有自己独立的副本。

面试官:“看你简历写了熟悉 Java 并发编程,那你给我讲讲 ThreadLocal 吧。”

我:“ThreadLocal 是 Java 中的一个类,用于创建线程局部变量。每个线程对 ThreadLocal 变量的访问都是独立的,每个线程都会拥有自己独立的副本。”

完蛋,这么唠,面试官一看就是背的八股,一点自己的东西都没有。

你得唠点面试官爱听的,要有一定深度,让面试官看出自己的思考。

下面我先来打个样。

我在实际开发项目有利用过 ThreadLocal 存储用户信息,并对 ThreadLocal 源码有过一定研究。

ThreadLocal 的优势是无锁化提升并发性能和简化变量的传递逻辑,每个线程对 ThreadLocal 变量的访问都是独立的,每个线程都会拥有自己独立的副本。

我:“需要我展开聊聊吗?”

面试官:“好的。”

ThreadLocal 存储的变量实际上是存储在 Thread 线程对象中,在 Thread 类中有两个 ThreadLocalMap 类型的变量一个是 threadLocals,另一个是 inheritableThreadLocals。

其中 threadLocals 就是用于存储 ThreadLocal 对应的变量。ThreadLocalMap 也是哈希数据结构,不过与我们用的最多的 HashMap 有所不同。

ThreadLocalMap 解决哈希冲突采用的是线性探测法,而 HashMap 采用的是拉链法。

ThreadLocalMap 的初始容量是 16,当负载达到 2/3 的时候会触发扩容逻辑,扩容的时候容量*2。

ThreadLocalMap 通过 Entry 数组存储数据。每一个 Entry 对象的 key 是一个弱引用指向的 ThreadLocal 对象。值是一个强引用的对象,类型由 ThreadLocal 对象的泛型 <> 决定。

在 Entry 对象中 ThreadLocal 之所以使用弱引用进行链接是为了减少当内存泄露发生时所带来的内存损失。

一旦 ThreadLocal 对象失去了外界强引用,在发生垃圾回收时仅被 Entry 对象弱引用的 ThreadLocal 对象就会被垃圾回收器回收,这时该 Entry 对象就是所谓的过时 Entry。

过时 Entry 自身及其引用的 vlaue 值在其它 ThreadLocal 对象执行 get、set、remove 方法时,可能会被清理,从而释放泄露的内存。

当然在实际开发中,我们更应该主动在恰当时机调用 remove 方法,对不再使用的 ThreadLocal 对象进行清理,避免触发 ThreadLocal 的清理机制,进而提升 get、set、remove 方法的执行效率。

需要注意的一点,我们通常所使用的 web 容器 tomcat 对工作线程的管理使用了池化技术,也就是说 Thread 对象会被重复使用。

如果我们在一个请求结束的时候没有调用 remove 方法清理 ThreadLocal 对象,并且其他请求在执行 get 方法前没有执行 set 方法进行设置值,那么可能会发生匪夷所思的业务异常。

咦?为什么当前是 A 发起的请求,获取到的却是 B 的信息?

图片图片

ThreadLocal 类有一个子类叫做 InheritableThreadLocal,InheritableThreadLocal 主要解决在单次请求过程中涉及到了多线程异步处理逻辑时,ThreadLocal 变量无法传递问题。

双十一有一个比价需求,需要拉取同商品在拼多多、淘宝、京东、抖音上的价格进行比价,如果采用单线程去执行,需要依次进行调用,该接口总耗时为获取各大平台价格耗时的累加和。

此时要想提高接口执行效率可以采取多线程方案,但是在获取各大平台优惠价时需要获取 ThreadLocal 中存储的上下文信息。

但是,我们前面说过 ThreadLocal 储存的上下文,其实是存储在了 Thread 对象中。

而采取多线程方案时,获取各大平台价格的线程并不是之前的主线程,这时是无法直接通过 ThreadLocal 的 get 方法获取到在主线程存储的上下文信息。

此时怎么办呢?有两种方案,第一种是在开启多线程之前先取出上下文信息,然后作为参数传递给每一个线程,但是这不是与 ThreadLocal 简化变量的传递逻辑的初衷相悖了吗?

通过 InheritableThreadLocal 就可以很好的解决这个问题,在线程初始化的时候,当前线程的 inheritableThreadLocals 会拷贝给新创建线程的 inheritableThreadLocals。

inheritableThreadLocals 就是上文提到的 Thread 线程对象中的另外一个 ThreadLocalMap 类型变量,用于存储 InheritableThreadLocal 记录的信息。

图片图片

我:“以上就是我对 ThreadLocal 的一些认识。”

内心甚至期盼着面试官继续深究,看过源码解读系列完全不慌!

想要进行一步了解 ThreadLocal 的小伙伴可以回看 ThreadLocal 源码解读系列。

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2024-09-24 10:28:22

2022-10-25 10:20:31

线程变量原理

2022-11-04 08:47:52

底层算法数据

2024-03-13 07:53:57

弱引用线程工具

2021-05-31 11:43:19

B-树MySQL索引

2021-06-02 10:23:06

索引B+树数据

2021-03-03 17:26:45

面试Synchronous底层

2015-08-13 10:29:12

面试面试官

2020-11-30 11:01:34

反射用途实现

2022-05-23 08:43:02

BigIntJavaScript内置对象

2010-08-12 16:28:35

面试官

2023-02-16 08:10:40

死锁线程

2021-12-25 22:31:10

MarkWord面试synchronize

2023-12-27 18:16:39

MVCC隔离级别幻读

2024-11-19 15:13:02

2018-10-22 14:28:26

面试官数据公司

2024-06-13 08:01:19

2021-11-08 09:18:01

CAS面试场景

2023-06-05 07:57:53

Kafka消息事务消息

2019-04-01 10:20:29

技术研发指标
点赞
收藏

51CTO技术栈公众号