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

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.lryc.cn/news/600955.html

相关文章:

  • SGLang 核心技术详解
  • 20250726-3-Kubernetes 网络-Service三种常用类型_笔记
  • 创建 Vue 项目的 4 种主流方式
  • 嵌入式——C语言:指针②
  • 智慧城市多目标追踪精度↑32%:陌讯动态融合算法实战解析
  • 【科普】java和html和lvgl生成页面有什么区别,还有什么方法可以生成?
  • Python深入 Tkinter 模块
  • OpHReda精准预测酶最佳PH
  • Ubuntu 22.04 配置 Zsh + Oh My Zsh + Powerlevel10k
  • dify前端应用相关
  • 超时进行报警例子
  • 成都陆军学校计算机科学学院编程马拉松活动计划书
  • linux线程概念和控制
  • java服务线程泄露临时解决脚本
  • .bat 打开方式恢复
  • QT中启用VIM后粘贴复制快捷键失效
  • CSS变量与Houdini自定义属性:解锁样式编程新维度
  • Aerospike架构深度解析:打造web级分布式应用的理想数据库
  • 数据科学与大数据技术专业的核心课程体系及发展路径全解析
  • TIM 输入捕获
  • 【AcWing 143题解】最大异或对
  • 秋招Day19 - 分布式 - 分布式事务
  • 15.6 DeepSpeed+Transformers实战:LLaMA-7B训练效率提升210%,显存直降73%
  • 复杂产品系统集成协同研发平台的研究与实现
  • MyBatis Plus 对数据表常用注解
  • 【C++基础】指针常量 | 常量指针 | int* p | const int* p | int* const p| const int* const p
  • 鼎捷T100程序开发(双档程序开发)
  • Unity 实现帧率(FPS)显示功能
  • 手写PPO_clip(FrozenLake环境)
  • 智慧水库管理系统中标签工厂的建立方案