ThreadLocal原理图解(看这篇就够了)

ThreadLocal原理图解(看这篇就够了)-mikechen

ThreadLocal的作用

当多个线程同时读写同一共享变量时存在并发问题,如下图所示:

ThreadLocal原理图解(看这篇就够了)-mikechen

如果不共享上图红色的资源,不就没有并发问题了。

也就是说:一个线程存一个自己的变量,类比原来好几个人玩同一个球,现在一个人一个球,那就没有并发问题了。

 

ThreadLocal原理

如何把变量存在线程上呢?如何把线程的数据隔离呢?下面我们继续详解ThreadLocal的原理。

因为一个线程内,可以存在多个 ThreadLocal 对象,所以其实是ThreadLocal内部维护了一个 Map 。

这个 Map 不是直接使用 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类,用来存变量。

它的大概结构如下图所示:

ThreadLocal原理图解(看这篇就够了)-mikechen

ThreadLocalMap是一个Map,key是ThreadLocal,value是Object。

映射到源码就是如下所示:

ThreadLocal原理图解(看这篇就够了)-mikechen

往ThreadLocalMap里面放值

ThreadLocal原理图解(看这篇就够了)-mikechen

从ThreadLocalMap里面取值

ThreadLocal原理图解(看这篇就够了)-mikechen
整体结构如下图所示:

ThreadLocal原理图解(看这篇就够了)-mikechen

实际上,ThreadLocalMap内部持有一个 Entry[] 类型的数组 table,每个数组成员都是一个键值对,Entry数组是真正承载数据的地方。

Entry继承自WeakReference,每个Entry 的 key 都是一个 ThreadLocal 对象的弱引用,value 是Object 类型,是强引用,ThreadLocalMap 中可以包含多个ThreadLocal对象。

如下图所示:

ThreadLocal原理图解(看这篇就够了)-mikechen

ThreadLocalMap中包含了多个ThreadLocal对象

那么如果一个线程使用多个ThreadLocal对象,ThreadLocalMap如何区分不同的ThreadLocal呢?

实际上,每一个ThreadLocal对象都包含了一个独一无二的threadLocalHashCode值,使用这个值就可以在KV数组中找到对应的本地变量。

ThreadLocal原理图解(看这篇就够了)-mikechen

key是ThreadLocal对象的弱引用,之所以使用 WeakReference 类型作为ThreadLocal对象的引用,是出于垃圾回收考虑。

当某个 ThreadLocal对象已经没有强引用指向时,它被 GC 回收,那么它的 ThreadLocalMap 里对应的 Entry 的键值会随之失效。

不过需要注意的是,虽然key值是弱引用,不影响ThreadLocal对象回收,但value值是强引用。

当ThreadLocal被回收,value对象不会被回收,记得要调用 remove() 方法避免内存泄露。

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法