[细品java]ThreadLocal源码学习
ThreadLocal是线程局部变量,其中保存了特定于该线程的值.每个线程都拥有一份独立的副本值,即每个线程修改变量值不影响其他线程该变量的副本值.这些特定于线程的值保存在Thread对象中,当线程终止后,这些值会作为垃圾回收.
如果没有看源码可能会认为ThreadLocal内部的实现方式应该是采用Map容器,保存一个<Thread,T>的映射关系.然而JDK内部并不是这么实现的,而是在Thread类中加入了一个散列表(ThreadLocalMap是ThreadLocal的静态内部类)来维护当前线程的所有局部变量值(即当前线程中的所有ThreadLocal变量),通过散列表数据结构可以快速地执行get和set操作.
散列
每次创建的ThreadLocal都有唯一的hashCode值,根据这个值hashCode可以计算变量在散列表中对应的地址.ThreadLocalMap的hash函数方法是将hashCode值和散列表容量大小进行与操作得到变量对应的位置.
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
冲突解决
在散列表中会存在地址冲突问题,即对于不同的hashCode可能会计算得到相同地址.对于这种情况,ThreadLocalMap采用了最简单的冲突解决方案---从冲突发生的地址d开始,依次探测d的下一个地址,直到找到一个空闲单元为止.
装填因子
ThreadLocalMap 设置的装填因子为2/3,当变量个数大于2/3时扩大容器容量再散列.
get和set
ThreadLocal的定义和使用比较简单,只要声明一个ThreadLocal变量,那该变量就为每个线程都分配了一个副本.通过set和get方法设置和获取该线程局部变量值:
ThreadLocal<Integer> integerLocal = new ThreadLocal<Integer>();
integerLocal.set(1);
Integer i = integerLocal.get();
对于以上的set和get方法的实现思路也比较简单.在set方法中,首先查看当前线程是否初始化了散列表,如果没有则创建一个散列表(初始化容器大小为16).如果存在则根据hashCode计算变量在散列表的地址并设置值.
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get方法和set方法相似,如果散列表不存在时则创建一个散列表并设置初始值.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 2025成都.NET开发者Connect圆满结束
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络