前言
作为多线程重要的组成部分之一 ThreadLocal 应该是大家所熟悉的, 哪怕不熟悉学习Java
的小伙伴也应该听说过, ThreadLocal
是线程变量, 他以ThreadLocal
对象为键, 任意对象为值的存储结构.
在Netty
中也有一个相似的类FastThreadLocal
用来处理频繁访问的场景, 这个类弥补了ThreadLocal
的诸多不足:
- 效率低下
- 容易出现
Hash
冲突 - 需要
O(n)
是时间复杂度解决问题
接下来我们就一起学习一下FastThreadLocal
本篇文章将添加于我的
Netty
专栏 欢迎大家关注
InternalThreadLocalMap
InternalThreadLocalMap
类似于ThreadLocalMap
, 主要用于线程内部变量的存储
ThreadLocal
的一个重要缺点就是ThreadLocalMap
采用线性探测法解决Hash
冲突问题, InternalThreadLocalMap
的主要重要作用也是解决这个问题
get() 方法
-
获取当前线程
- 如果当前线程实现了
FastThreadLocalThread
类, 那么直接调用fastGet()
方法获取到FastThreadLocalThread
的threadLocalMap
属性 - 如果当前线程没有实现
FastThreadLocalThread
类, 那么调用slowGet()
方法
- 如果当前线程实现了
执行到
slowGet()
方法类似于一种对当前操作的兜底方案, 如果当前线程不是FastThreadLocalThread
, 内部是没有InternalThreadLocalMap
属性的, 此时获取就变成了获取 JDK 原生的ThreadLocal
FastThreadLocal
在方法适用性上, Netty
做的非常好, 使用FastThreadLocal
和使用 JDK 原生的ThreadLocal
几乎没有区别, 方法基本保持一致
get() 方法
-
首先去获取当前线程的
ThreadLocalMap
对象 -
执行
indexedVariable(idnex)
方法, 从数组中取出下标为index
的对象 -
判断取出的对象是不是缺省对象
- 是缺省对象
UNSET
, 说明这个位置已经填充过数据, 直接取出返回 - 不是缺省对象
UNSET
, 进行初始化操作
- 是缺省对象
set()方法
-
判断
value
是否为缺省值UNSET
- 如果是缺省值, 调用
remove()
方法 - 如果不是缺省值, 获取到当前的
ThreadLocalMap
, 然后调用 setKnownNotUnset(threadLocalMap, value) 方法, 将ThreadLocalMap
中的数据替换为value
- 如果是缺省值, 调用
setKnownNotUnset(threadLocalMap, value)
在这个方法中一共做了两件事:
-
首先获取数组下标 index, 并在该下标设置新的 value
-
如果成功
- 则将当前的 FastThreadLocalThread(this) 保存到 Set 中
setIndexedVariable(int index, Object value)
-
在这里, 我们先去获取了当前的数组的长度, 判断下标和当前数组的容量大小
- 如果容量不够则进行扩容方法
- 如果容量充足, 找到数组
index
下标位置将新的value
设置进去, 时间复杂度 O(1), 同时如果旧的数据是缺省的UNSET
则返回 true
下图是扩容方法详情图, 如果看过HashMap
源码的同学应该会感觉比较熟悉, 他也是将容量向上取整为 2 次幂扩容, 然后将原数组内容拷贝到新数组
HashMap
扩容方法如下
addToVariablesToRemove()
-
获取下标为 0 的元素
-
判断这个元素是否为缺省或者为 null
- 如果是的话, 创建
FastThreadLocal
类型的Set
集合, 将Set
集合填充到数组下标为 0 的位置 - 如果不是的话, 代表
Set
集合已存在, 直接强制转换获得Set
集合
- 如果是的话, 创建
-
将
FastThreadLocal
添加到Set
集合中
总结
FastThreadLocal
我们就学到这里了
关于这个类的高效查找我们也看到了是因为 JDK 原生的 ThreadLocal
是 Hash 查找 ,而FastThreadLocal
是 index 查找, 高效的同时避免了 Hash 冲突
本文内容到此结束了
如有收获欢迎点赞?收藏?关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问?欢迎各位大佬指出。
我是 宁轩 , 我们下次再见