梦里向Thread请教ThreadLocal

某个深夜,我在写代码的时候,需要用到一个结构,这个结构的作用很简单,他是跟着线程走的,对不同线程来说,他是天然隔离的,我冥思苦想了很久,直至进入了梦想。

在梦里发生了一件有意思的事情,酷拉皮卡在全职猎人中遇见了一个新的考核,需要自学编程

于是酷拉皮卡用具象化的能力将Thread用具象化了

于是发生了一场人与线程的对话。

「帅气的饭饭:」 你就是Thread本人?

「Thread:」 是啊,我因为酷拉皮卡的能力具象化了,现在能说会跳,还能rap。

「帅气的饭饭:」  我擦,我一定是做梦了,赶紧问这扑街问题,趁梦还没醒,把排期给解决了。是这样的,我这边遇见了一个需求,需要用到一个结构,这个结构的作用很简单,他是跟着线程走的,对不同线程来说,他是天然隔离的,冥思苦想了很久,仍旧没有找到答案,你知道吗?

「Thread:」 知道啊,这不是很简单吗, 你这个辣鸡。可以使用ThreadLocal啊,他是一个本地线程副本变量工具类,主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,适用于各个线程不共享变量值的操作,具备天然隔离的能力。

「帅气的饭饭:」 对对对,就是ThreadLocal,这满足我的需求,那他的原理是什么样呢?

「Thread:」 ThreadLocal原理很简单啊,你怎么和那些懒得动手的人一样啊,百度下就知道的问题啊,直接问来的香吗?算了,告诉你好了,听好了

每个线程的内部都维护了一个 ThreadLocalMap,它是一个 Map(key,value)数据格式,key 是一个弱引用,也就是 ThreadLocal 本身,而 value 存的是线程变量的值。

也就是说 ThreadLocal 本身并不存储线程的变量值,它只是一个工具,用来维护线程内部的 Map,帮助存和取变量。

数据结构,你可以看看:

「帅气的饭饭:」 懂了懂了,原来如此,竟然内部是一个Map结构,那怎么解决hash冲突啊?

「Thread:」 这倒是把我问倒了,等下,我打电话给ThreadLocal,他也被酷拉皮卡具象化了。

哦哦哦,知道了。

与 HashMap 不同,ThreadLocalMap 结构非常简单,没有 next 引用,也就是说 ThreadLocalMap 中解决 Hash 冲突的方式并非链表的方式,而是采用线性探测的方式。所谓线性探测,就是根据初始 key 的 hashcode 值确定元素在 table 数组中的位置,如果发现这个位置上已经被其他的 key 值占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。

源代码实现如下:

/
 * Increment i modulo len.
 */
private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

/
 * Decrement i modulo len.
 */
private static int prevIndex(int i, int len) {
    return ((i - 1 >= 0) ? i - 1 : len - 1);
}

「帅气的饭饭:」 哦,明白了,你刚刚说Key是弱引用?为什么这么设计啊?

「Therad:」 刚好我刚刚问了ThreadLocal老哥了,是这样的

为什么要这样设计,这样分为两种情况来讨论:

  • key 使用强引用:这样会导致一个问题,引用的 ThreadLocal 的对象被回收了,但是 ThreadLocalMap 还持有 ThreadLocal 的强引用,如果没有手动删除,ThreadLocal 不会被回收,则会导致内存泄漏。

  • key 使用弱引用:这样的话,引用的 ThreadLocal 的对象被回收了,由于 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使没有手动删除,ThreadLocal 也会被回收。value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候会被清除。

比较以上两种情况,我们可以发现:由于 ThreadLocalMap 的生命周期跟 Thread 一样长,如果都没有手动删除对应 key,都会导致内存泄漏,但是使用弱引用可以多一层保障,弱引用 ThreadLocal 不会内存泄漏,对应的 value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候被清除,算是最优的解决方案。

「帅气的饭饭:」 嗯,明白了,那使用ThreadPool有没有要注意的地方呢?

「Thread:」 有的,要尽量避免内存泄露哟

「帅气的饭饭:」 啥?为什么会内存泄露

「Thread:」 你是不是傻啊?

ThreadLocal 在 ThreadLocalMap 中是以一个弱引用身份被 Entry 中的 Key 引用的,因此如果 ThreadLocal 没有外部强引用来引用它,那么 ThreadLocal 会在下次 JVM 垃圾收集时被回收。这个时候 Entry 中的 key 已经被回收,但是 value 又是一强引用不会被垃圾收集器回收,这样 ThreadLocal 的线程如果一直持续运行,value 就一直得不到回收,这样就会发生内存泄露啊。

「帅气的饭饭:」 (⊙o⊙)…,可怕,那怎么避免啊?

「Thread:」 这么简单的问题都不知道啊,好吧,解决方法是 ......

特么,突然老母亲养的老母鸡打鸣把我吵醒了 ......

推荐《好好面试》系列文目前已经连载29篇啦,

相信我,这是一个不管你在职、求职,都值得关注的一个系列。

往期推荐

什么?HashMap竟然也有懒加载?

万万没想到!!! 谷歌面试原来也问ArrayList

为什么要破坏双亲委派模型,它不香吗?

阿里高频面试题,热部署了解吗?

Σ(っ°Д°;)っ找个对象"Object"还要用八股文?

《面试八股文》之Dubbo17卷

第一次面试,我差点被面试官打,就因为Collections.sort

高级开发竟然被构造器循环依赖难住了?

肥肥的主管和帅气的小饭饭讨论了下ForkJoinPool

聊聊Autowired的常考面试题

面试官告诉你什么是JMM和常考面试题

去年面了多个候选人,看看我挖的坑还有他们应该要补的Java基础(二)

去年面了多个候选人,看看我挖的坑还有他们应该要补的Java基础(一)

list中for循环删除多个元素为何报错?

手把手带你阅读源码,看看IoC容器的实现

【好好面试】学完Aop,连动态代理的原理都不懂?

你必须要懂的Spring-Aop之源码跟踪分析Aop

【好好面试】你必须要懂的Spring-Aop

你所不知道的HelloWorld背后的原理

连引用都答不上,凭什么说你是Java服务端开发

小饭饭:某游戏大厂高级开发,专门和主管抬杠的小组长。

目前写了三个系列文《全网最全的Caffeine教程》《一起玩dubbo》《好好面试》《好好面试》分享了自己多年来面试别人和被别人面试的经验,有兴趣看面经、学dubbo和Caffeine的可以微信搜:稀饭下雪

给这篇有趣、有用的文章点个在看!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值