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() 的第一要义是重置线程上下文,确保数据安全隔离。

相关推荐
写后端的胖头鱼2 小时前
一文详解CAS
java·jvm·spring·cas·乐观锁·自旋
鱼宵2 小时前
Spring AI 提示词模板:{变量} 参数化 + few-shot,一条提示词反复用
java·人工智能·spring·few-shot·提示词工程·springai
蜗牛互联网2 小时前
Python消费Responses SSE事件:增量文本、超时与取消
java·开发语言·人工智能·后端·python
wanderist.2 小时前
线性筛法详解:从筛质数到欧拉函数、Möbius 函数与约数函数
java·数据结构·算法
Escalating_xu3 小时前
【C 语言】深入理解指针(5):sizeof、strlen、数组名语义与指针笔试题全解析
java·c语言·开发语言
HEJOO94 小时前
Linux 设备驱动系列——使用全局工作队列
java·linux·算法
谢亮_vipxieliang4 小时前
Go Channel 高级模式——从底层原理到扇出扇入实战
java·数据库·golang
Code_Solitude4 小时前
c语言:递归
java·c语言·算法
Nebula_g4 小时前
JavaSE加强:Stream流
java·开发语言·windows·stream·javase·流