当前位置: 首页 > web >正文

ThreadLocal--ThreadLocal介绍

🧠 一、什么是 ThreadLocal

  • ThreadLocal 是 Java 提供的一种 线程本地变量机制

  • 每个线程都维护一份自己的副本;

  • 它不用于多个线程共享变量,而是用于每个线程独立维护自己的变量副本

  • 常用于:用户上下文、数据库连接、格式化对象(如 SimpleDateFormat)、日志跟踪等场景。


🚀 二、ThreadLocal 的基本用法

ThreadLocal<String> local = new ThreadLocal<>();
local.set("hello");      // 设置当前线程的副本
String val = local.get(); // 获取当前线程的副本
local.remove();           // 手动删除,防止内存泄漏

每个线程访问的是 自己的变量副本,彼此隔离。


🧱 三、ThreadThreadLocalThreadLocalMap 三者关系图

Thread (线程对象)└── ThreadLocalMap (每个线程独有的 map)└── Entry[] 数组├── key:ThreadLocal 对象(弱引用)└── value:真正的变量值

✅ 总结对应关系:

角色说明
Thread每个线程都有一个 ThreadLocalMap
ThreadLocal作为 key 存在于 ThreadLocalMap 中,指向当前线程的副本
ThreadLocalMapThread 内部的属性,负责存储每个 ThreadLocal 对应的数据

🎯 四、为什么 ThreadLocalMap 的 key 是 弱引用

这个想要了解更详细可以看博主的另一篇博客:ThreadLocal--ThreadLocal 竟可能导致内存泄漏?看看 ThreadLocalMap 的弱引用机制-CSDN博客

✅ Java 源码:

static class Entry extends WeakReference<ThreadLocal<?>> {Object value;
}

✅ 原因:防止内存泄漏(重点)

  • 如果 ThreadLocal强引用

    • 即使我们不再使用 ThreadLocal,它依然会作为 key 强引用存在,永远不会被 GC;

    • 而且 ThreadLocalMap 属于 ThreadThread 不结束就不会释放内存;

    • 久而久之,value 也无法回收,造成 内存泄漏

✅ 如果是 弱引用

  • 当开发者不再持有 ThreadLocal 引用时,它会被 GC 回收;

  • GC 后 ThreadLocalMap 中 key 为 null;

  • 如果调用 ThreadLocal.get() / set(),会清除掉这些 stale entry(陈旧数据);

  • ✅ 避免内存泄漏。


💣 五、ThreadLocal 内存泄漏陷阱

  • 问题场景:

    • 线程池中线程长时间不销毁;

    • ThreadLocal 被 GC 回收,但 ThreadLocalMap 的 value 还存在;

    • 如果不调用 .remove(),value 永远不会清理;

  • 解决方式:

    • ✅ 使用完后调用 ThreadLocal.remove() 清理;

    • ✅ 或者用 try-finally 包装使用逻辑:

private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));public void parseDate(String dateStr) {try {formatter.get().parse(dateStr);} finally {formatter.remove(); // 手动清除,避免内存泄漏}
}

🧰 六、ThreadLocalMap 的实现细节

  • 本质上是一个自定义的哈希表:

    • 数组结构 + 开放寻址法(冲突后线性探测)

  • 不是 HashMap,也不是 ConcurrentHashMap

  • 数组大小默认 16,按需扩容(最多 2^30)


🛠 七、常用方法详解

方法说明
set(T value)设置当前线程副本中的值
get()获取当前线程副本中的值
remove()删除当前线程副本中的值
withInitial(Supplier)构造带默认初始值的 ThreadLocal


✅ 示例:使用默认初始值的 ThreadLocal

ThreadLocal<Integer> counter = ThreadLocal.withInitial(() -> 0);public void increment() {counter.set(counter.get() + 1);
}

🌟 八、InheritableThreadLocal:子线程继承父线程值

InheritableThreadLocal<String> local = new InheritableThreadLocal<>();
local.set("父线程值");new Thread(() -> {System.out.println(local.get()); // 子线程能读取父线程设置的值
}).start();

适合:父线程传递上下文,如用户ID、请求ID 等。


🧭 九、实际应用场景

场景示例
✅ 用户上下文登录后存放用户信息:ThreadLocal<User>
✅ DateFormatSimpleDateFormat 非线程安全,放入 ThreadLocal
✅ 数据源切换动态数据源管理,存放在 ThreadLocal
✅ Trace ID日志链路追踪,全链路唯一 ID 存 ThreadLocal
✅ Spring事务/安全Spring 的 TransactionSynchronizationManagerSecurityContextHolder 都用到了 ThreadLocal

📌 十、总结

项目内容
本质每个线程一个变量副本
原理每个线程有一个 ThreadLocalMap
结构key 为弱引用的 ThreadLocal,value 为副本值
弱引用原因防止内存泄漏,GC 后 key=null 自动清理
使用建议用完及时调用 remove()
延伸功能InheritableThreadLocal 实现值传递
应用场景用户信息、日期格式化、日志追踪、数据库连接等
http://www.xdnf.cn/news/16482.html

相关文章:

  • 技术 — 资本双螺旋:AI 时代的投资浪潮与技术突破
  • vulhub-earth靶机攻略
  • Mixture-of-Recursions: 混合递归模型,通过学习动态递归深度,以实现对自适应Token级计算的有效适配
  • 什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?
  • QT中启用VIM后粘贴复制快捷键失效
  • SQL Developer Data Modeler:一款免费跨平台的数据库建模工具
  • Linux c++ CMake常用操作
  • 数字迷雾中的安全锚点:解码匿名化与假名化的法律边界与商业价值
  • 融合为体,AI为用:数据库在智能时代的破局之道
  • petalinux配置总结
  • 卫星通信终端天线对星之:参考星对星
  • SSE (Server-Sent Events) 服务出现连接卡在 pending 状态的原因
  • 数据库HB OB mysql ck startrocks, ES存储特点,以及应用场景
  • 树上倍增和LCA问题
  • 【Zephyr】Window下的Zephyr编译和使用
  • 数学建模国赛历年赛题与优秀论文学习思路
  • 考研复习-数据结构-第八章-排序
  • 洛谷 P1217:[USACO1.5] 回文质数 Prime Palindromes
  • Android开发中线上crash问题解决流程
  • [spring6: Mvc-函数式编程]-源码解析
  • 栈----2.最小栈
  • 单元测试、系统测试、集成测试知识详解
  • 面试150 只出现一次的数字
  • Java I/O知识归纳
  • 字符串操作
  • ESP32实战:5分钟实现PC远程控制LED灯
  • AI Agent笔记--读腾讯技术公众号
  • dify前端应用相关
  • Java中List集合对象去重及按属性去重
  • 学习随想录-- web3学习入门计划