ThreadLocal

ThreadLocal 线程本地变量,对象的存储是在线程开始时分配,线程结束时回收,每个线程有该对象自己的实例,线程之间隔离。

这种对象的链接性(linkage)可以是静态的也可是外部的。但ThreadLocal变量通常是私有,静态。

原因: 防止每一个线程或对象重复创建 ThreadLocal实例。ThreadLocal本质上只是作为访问ThreadLocalMap的 Key,全局只需要一份静态 Key 即可。

ThreadLocal使用场景:

在并发场景下的多线程修改共享变量时,会出现线程安全问题

这里可以用锁实现

但是当并发量增大的时候,锁显然不是一个好方法。

在线程内部,共享上下文数据,避免多层方法不停传递参数;并且天然做到线程之间数据隔离。

ThreadLocal又是如何实现县城隔离的呢?

数据部署存在ThreadLocal对象上,而是存在每个Thread线程对象内部的 ThreadLocalMap 每个线程都拥有自己独立的Map,所以能天然隔离。

哈希冲突问题

ThreadLocal解决哈希冲突问题并没有使用 HashMap 的链表/红黑树 解决哈希冲突 而是线性探测法:

当计算出的数组下标已经占用时,ThreadLocalMap 会依次查找下一个位置(i + 1),直到找到空位或待更新的节点。

Key 为什么设计成弱引用?

复制代码
static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value; // value 是强引用!
    Entry(ThreadLocal<?> k, Object v) {
        super(k); // key 是弱引用!
        value = v;
    }
}

如果Key为强引用:

即使ThroadLocal为空,Entry依然被强引用,导致ThreadLocal对象无法在GC时被回收,引发内存泄漏

如果Key为弱引用:

当业务代码的ThreadLocal为空,下次JVM进行GC时,无视弱引用,ThreadLocal对象会被立即回收。

Value为什么不是弱引用?

如果Value是弱引用,可能代码里还没有用Value(没有强引用指向Value)gc之后就会错误的把Value回收

这样后面就用不了了,所以Value必须是强引用。所以Value是无法自动回收的,导致Value内存泄漏,所以用完ThreadLocal之后必须调用remove方法,断开Value引用。

内存泄漏

为防止内存泄漏 必须要进行remove回收

复制代码
public void handleRequest(User user) {
    userContextThreadLocal.set(user);
    try {
        // 执行核心业务逻辑
        doBusiness();
    } finally {
        // 确保在请求结束时彻底清除线程本地变量,规避内存泄漏与线程复用带来的数据脏读
        userContextThreadLocal.remove();
    }
}

既然忘记 remove() 都会导致内存泄漏,为什么不全用强引用并强制要求开发者 remove() ??

若外部对 ThreadLocal 的引用切断,但 ThreadLocalMap 仍持强引用,Key 永远不为 null。ThreadLocalMap 无法判断数据是否废弃,100% 造成永久泄漏。

既然 Key 变成 null 会被清理,为什么必须显式 remove()?

JDK 的脏节点清理(expungeStaleEntry)不会主动定时运行,必须依赖当前线程下一次调用 get()、set() 或 remove() 顺带触发。

如果该线程随后归还给线程池且长时间无任务,或者后续任务不再操作该 ThreadLocal,Value 将一直停留在内存中无法释放。

而且下一次使用时,若未设置新值而直接 get(),会直接读取到上一个对象的敏感数据。

手动 remove() 的第一要义是重置线程上下文,确保数据安全隔离。

相关推荐
天远Date Lab2 分钟前
零信任架构实战:基于天远企业股权变更构建自动化招投标供应商股东穿透审查网关
java·人工智能·架构·自动化
weixin_3993806924 分钟前
TongWeb7.0.4.9M11如何接入到idea(by lqw)
java·ide·intellij-idea
paopaokaka_luck36 分钟前
社区服务平台(加权协同过滤推荐、Jieba中文词频分析、WebSocket实时聊天、求助处理闭环、 活动积分闭环、ECharts多维分析)
java·前端·spring boot·mysql·echarts·mybatis
.Bryce.43 分钟前
T2-LLM评测不等于Agent评测
android·java·人工智能·后端·gpt·agent·1024程序员节
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 用户体系集成:查询用户环信账号密码
java·交友
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 发布评论
java·开发语言·交友
qq_452396231 小时前
第八篇:《VACUUM、自动清理与膨胀治理》
jvm·数据库·oracle
qq_364682151 小时前
录音APP怎么切换语言?多语言版本如何使用
java·服务器·前端
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 用户体系集成:根据环信id查询用户详情
java·lua·交友
摇滚侠2 小时前
《On Java 中文版 基础卷》阅读笔记 复用 13
java·笔记·python