来源 | wuqinduo
juejin.im/post/5dcb58f2f265da4d3f44c5a1
ThreadLocal
用来提供线程级别变量,变量只对当前线程可见。相比与“使用锁控制共享变量访问顺序”的解决方案。 ThreadLocal
通过 空间换时间 的方案,规避了竞争问题,因为每个线程都有属于自己的变量。
此时就产生了第一个问题: 线程如何维护属于自己的变量副本 ,搞懂了这个也就搞懂了其原理。
Thread
中有一个 threadLocals
属性表示线程的本地变量。这个属性的类型是 ThreadLocal.ThreadLocalMap
ThreadLocalMap
是 ThreadLocal
的内部类,他是类 Map
结构,也是存储 K-V
结构数据,并用 Entry
封装 K-V
。不同的是 ThreadLocalMap
的 Entry
的 Key
只能是 ThreadLocal
类型对象,并且是一个弱引用。
也就是说线程通过一个类Map数据结构 ThreadLocal.ThreadLocalMap
来存储属于自己的线程变量。
ThreadLocalMap
赋值、取值操作的入口在其外部类 ThreadLocal
中。
set(v)
方法内调用 ThreadLocalMap
的 set(this,v)
方法存值。(类似 Map
的 put(k,v)
方法)
get()
方法内调用 ThreadLocalMap
的 getEntry(this)
方法取值(类似 Map
的 get(k)
方法)
通过代码可以看出:
第一次操作线程的 ThreadLocalMap
属性时,会初始化一个 ThreadLocal.ThreadLocalMap
, set(v)
会存入以参数为 Value
的 K/V
数据, get()
会存入以 null
为 value
的 K/V
数据。
ThreadLocal.ThreadLocalMap
存值操作入口是 ThreadLocal.set(v)
方法,并以当前 ThreadLocal
变量为 key
,参数为 value
。
ThreadLocal.ThreadLocalMap
取值操作入口是 ThreadLocal.get(v)
方法, key
为当前ThreadLocal变量。
我们在从代码层面直观的体会这个操作:
至此线程的本地变量的本质就清晰了。就是 Thread
用类似 Map
的 ThreadLocal.ThreadLocalMap
数据结构来存储以 ThreadLocal
类型的变量为 Key
的数值,并用 ThreadLocal
来存取删,操作 ThreadLocalMap
。
当我们定义一个 ThreadLocal
变量时,其实就是在定义一个 Key
当我们调用 set(v)
方法时,就是以当前 ThreadLocal
变量为 key
,传入参数为 value
,向 ThreadLocal.ThreadLocalMap
存数据
当我们调用 get()
方法时,就是以当前 ThreadLocal
变量为 key
,从 ThreadLocal.ThreadLocalMap
取对应的数据
采用 线性探测 的方式,根据 key
计算 hash
值,如果出现冲突,则向后探测,当到哈希表末尾的时候再从0开始,直到找到一个合适的位置。
这种算法也决定了 ThreadLocalMap
不适合存储大量数据。
ThreadLocalMap
初始大小为 16
,加载因子为 2/3
,当 size
大于 threshold
时,就会进行扩容。
扩容时,新建一个大小为原来数组长度的 两倍 的数组,然后遍历旧数组中的 entry
并将其插入到新的hash数组中,在扩容的时候,会把 key
为 null
的 Entry
的 value
值设置为 null
,以便内存回收,减少内存泄漏问题。
后 记
若有错误或者不当之处,可在本公众号内反馈,一起学习交流!
更多热文在此:
● Spring Boot 系列实战文章合集(源码已开源)
● 程序员写简历时必须注意的技术词汇拼写
● 前后端都分离了,该搞个好用的API管理系统了!
● 从一份配置清单详解Nginx服务器配置
● 如何在Windows下像Mac一样优雅的开发
● Docker容器可视化监控中心搭建
● 利用ELK搭建Docker容器化应用日志中心
● 真实IT领域2/8法则,扎心了!
● 一文详解 Linux系统常用监控工具
更多 务实、能看懂、可复现的 技术文章、资源尽在公众号 CodeSheep ,欢迎扫码订阅,第一时间获取更新 :arrow_down::arrow_down::arrow_down: