某个深夜,我在写代码的时候,需要用到一个结构,这个结构的作用很简单,他是跟着线程走的,对不同线程来说,他是天然隔离的,我冥思苦想了很久,直至进入了梦想。
在梦里发生了一件有意思的事情,酷拉皮卡在全职猎人中遇见了一个新的考核,需要自学编程
于是酷拉皮卡用具象化的能力将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篇啦,
相信我,这是一个不管你在职、求职,都值得关注的一个系列。
往期推荐
第一次面试,我差点被面试官打,就因为Collections.sort
去年面了多个候选人,看看我挖的坑还有他们应该要补的Java基础(二)
去年面了多个候选人,看看我挖的坑还有他们应该要补的Java基础(一)
小饭饭:某游戏大厂高级开发,专门和主管抬杠的小组长。
目前写了三个系列文《全网最全的Caffeine教程》、《一起玩dubbo》和《好好面试》,《好好面试》分享了自己多年来面试别人和被别人面试的经验,有兴趣看面经、学dubbo和Caffeine的可以微信搜:稀饭下雪
给这篇有趣、有用的文章点个在看!!!