ThreadLocal

什么是ThreadLocal?

ThreadLocalJava中一个非常重要的线程封闭工具,它用于创建线程局部变量 。每个线程都有自己独立初始化的变量副本 ,从而避免了多线程环境下的共享和同步问题。

ThreadLocal实现原理

set方法

java 复制代码
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

从这个方法能够看出,set方法是根据当前线程对象去取出一个ThreadLocalMap对象,实际的数据是放在这个ThreadLocalMap对象中的。键是ThreadLocal对象,值是传入的参数。

java 复制代码
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

getMap这个方法可以看出,这个ThreadLocalMap对象是线程对象的一个数据对象。

get方法

java 复制代码
    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();
    }

get方法依然是先获取线程中的ThreadLocalMap,然后以ThreadLocal对象为键,查找对应的值。

setInitialValue是一个兜底操作,放入一个初始化的值,并返回,默认是返回null,可以重写返回一个实际的值。

数据清理

从上面可以看出,数据实际上是存在线程中的,当线程不退出的情况下,对象的引用将一致存在。

当线程退出的时候,会做一些清理操作,其中包括清理ThreadLocalMap

java 复制代码
    private void exit() {
        if (threadLocals != null && TerminatingThreadLocal.REGISTRY.isPresent()) {
            TerminatingThreadLocal.threadTerminated();
        }
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

因此,使用线程池的时候,当前线程会保留在线程池中,如果将一个比较大的对象设置在ThreadLocal中,可能会出现内存泄漏。

因此要及时回收对象,使用ThreadLocal对象的remove方法。

ThreadLocalMap

ThreadLocalMap中的Entry实现了弱引用

java 复制代码
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

而且,值得注意的是,这个Entry与一般的Entry不同它仅有value,而没有key

ThreadLocalMap中传入的键成为了一个弱引用对象。

为什么要将键设置成为一个弱引用对象?

这是为了避免内存泄漏,防止ThreadLocal对象本身的内存泄漏 。 因为ThreadLocalMap是在线程中的,如果不使用弱引用,当外部的ThreadLocal强引用消失后,Entry这里还在强引用ThreadLocal对象,导致这个对象无法被GC。因此设计为弱引用,解决了键的泄漏问题

内存泄漏

ThreadLocal会出现内存泄漏,其根本原因在于ThreadLocalMap中的Entry中键是弱引用,而值是强引用 。这里的泄漏是值泄漏

在正常情况下:

  • ThreadLocal tl = new ThreadLocal();创建一个 tl对象。
  • 线程通过 tl.set(value)存储数据。
  • tl不再被需要时,tl变量置为 nulltl = null;)。
  • 由于 Entry 的 Key 是弱引用,在下次 GC 时,这个 Key 会被回收,Entry 的 Key 变为 null
  • 当下次操作 ThreadLocalMap(如 set、get、remove)时,Map 会自动清理这些 key==null的条目(惰性清理)。
  • 清理后,Value 的强引用断开,可以被正常回收。

在泄漏的情况(常见于线程池)::

  1. 强引用消失tl变量被置为 null,堆中的 ThreadLocal 对象只被 Entry 的 Key 弱引用着。
  2. Key 被回收 :GC 发生时,由于弱引用的特性,堆中的 ThreadLocal 对象被回收。Entry 中的 Key 变为 null,但 Value 依然被 Entry 强引用
  3. 线程未终止 :如果这个线程是线程池中的核心线程,它会一直存活(与线程池同生命周期),那么它的 ThreadLocalMap会一直存在。
  4. Value 无法释放 :这个 key=null的 Entry 及其 Value 对象,由于线程的强引用链(Thread -> ThreadLocalMap -> Entry -> Value)一直存在,只要线程存活,即使你再也无法通过任何代码访问到这个 Value(因为 Key 没了,get不到),它也无法被回收。这就造成了内存泄漏
  5. 更糟的情况:如果这个 Value 本身又间接引用了其他大对象,会导致一连串的对象都无法被回收。

泄漏主要是ThreadLocalMap的惰性清理没有触发。

相关推荐
二哈赛车手3 小时前
新人笔记---ApiFox的一些常见使用出错
java·笔记·spring
栗子~~4 小时前
JAVA - 二层缓存设计(本地缓冲+redis缓冲+广播所有本地缓冲失效) demo
java·redis·缓存
YDS8294 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— RAG知识库的搭建和接口实现
java·ai·springboot·agent·rag·deepseek
candyTong5 小时前
Claude Code 的 Edit 工具是怎么工作的
javascript·后端·架构
未若君雅裁5 小时前
MyBatis 一级缓存、二级缓存与清理机制
java·缓存·mybatis
AI人工智能+电脑小能手6 小时前
【大白话说Java面试题 第65题】【JVM篇】第25题:谈谈对 OOM 的认识
java·开发语言·jvm
GetcharZp6 小时前
GitHub 2.4 万 Star!D2 正在重新定义程序员画图方式
后端
阿维的博客日记6 小时前
Nacos 为什么能让配置动态生效?(涉及 @RefreshScope 注解)
java·spring
雨辰AI6 小时前
SpringBoot3 + 人大金仓读写分离 + 分库分表 + 集群高可用 全栈实战
java·数据库·mysql·政务
辰海Coding7 小时前
MiniSpring框架学习-完成的 IoC 容器
java·spring boot·学习·架构