ThreadLocal详解

一、底层原理

1.数据结构
  • 每个 Thread 内部维护一个 ThreadLocalMap(类似 HashMap)。
  • ThreadLocalMap 的 Key 是 ThreadLocal 实例(弱引用),Value 是存储的值(强引用)。
2.核心操作
ThreadLocalMap类结构:
复制代码
static class ThreadLocalMap {

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

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

         private Entry[] table;

        // ......
}



class Thread implements Runnable {
         // ......
        ThreadLocal.ThreadLocalMap threadLocals = null;
         // ......

}

ThreadLocalMap内部是Entry数组,Entry的k为当前线程且为弱引用,同时为线程类Thread的内部属性。

set()方法:
复制代码
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = t.threadLocals;
    if (map != null) {
        map.set(this, value); // this 指当前ThreadLocal对象
    } else {
        t.threadLocals = new ThreadLocalMap(this, value);
    }
}

set时先获取当前线程的ThreadLocalMap,用当前线程作为key进行赋值。

如果当前线程的threadLocals为空,就会new ThreadLocalMap(this, value) 绑定当前线程。

get()方法:
复制代码
    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();
    }

二、相关问题

1.内存泄漏
  • 原因:

1:ThreadLocalMap 的 Key(ThreadLocal)是弱引用,会被 GC 回收,导致 key=null 的 Entry。

2:Value 是强引用,线程未结束则 Value 无法回收。

  • 解决方案:

每次使用完必须调用 remove() 清理 Entry。

try {
threadLocal.set(data);
// ... 业务逻辑
} finally {
threadLocal.remove(); // 强制清除
}

2.脏数据

  • 原因:

线程池复用线程时,未清理 ThreadLocal 导致残留旧数据。

  • 解决方案:

同上,用 try-finally 确保 remove()。

3.弱引用 Key 的清理

  • 优化机制:

ThreadLocalMap 在 set()/get() 时会清理 key=null 的 Entry,但依赖调用时机。

  • 主动防护:

不要依赖自动清理,显式调用 remove()。

4.子线程继承问题

默认行为:子线程无法获取父线程的 ThreadLocal 值。

解决方案 :使用 InheritableThreadLocal(创建子线程时复制父线程数据)。

相关推荐
道尔柯南几秒前
C++ 面向对象编程入门:类和对象(上)
开发语言·c++·算法
老三牛擦4 分钟前
订单履约流程
jvm
楠楠子呀9 分钟前
英语、西语、阿语客户各开一个口:多语言获客的分流方案
开发语言·javascript·ecmascript·跨境电商·whatsapp·营销工具
tachibana217 分钟前
使用for range 的时候,它的地址会发生变化吗?
开发语言·后端·golang·go·语法
蜗牛互联网22 分钟前
GPT-6 与 Intelligent UI:从回答到可审计操作的产品迁移
java·人工智能·后端
临床数据科学和人工智能兴趣组1 小时前
正则表达式的基础语法的基本符号
开发语言·人工智能·python·正则表达式·r语言·r语言-4.2.1·workbudyy
王中阳Go1 小时前
Go 后端转 AI:非科班怎么补?一张 3 列路线表(可保存)
开发语言·人工智能·后端·python·golang·大语言模型
SL_staff1 小时前
运维救火实录:钉钉考勤API变更引发工资计算异常,我们用JVS-Logic可视化逻辑引擎实现分钟级热修复
java·mysql·全栈
SunnyDays10111 小时前
Java 查找并高亮 Word 文字:精确查找及正则表达式模糊查找
java·查找文字·word 文档·高亮文字·正则匹配查找·模糊查找
geats人山人海1 小时前
java 4继承
java·开发语言