【Java后端基础 005】ThreadLocal-线程数据共享和安全
📚博客主页:代码探秘者
✨专栏:文章正在持续更新ing…
✅C语言/C++:C++(详细版) 数据结构) 十大排序算法
✅Java基础:JavaSE基础 面向对象大合集 JavaSE进阶 Java版数据结构JDK新特性
✅前端基础: 前端三剑客 必学前端技术栈
✅后端经典框架: 后端基础 SpringBoot Tlias项目(含SSM)
✅数据库:Mysql
✅常用中间件:redis入门+实战 Elasticsearch RabbitMQ
✅Linux: 部署篇
✅微服务:微服务
❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更新的动力❤️
🙏作者水平有限,欢迎各位大佬指点,相互学习进步!
文章目录
线程数据共享和安全 -ThreadLocal
🧠1. 什么是 ThreadLocal?
ThreadLocal
是 Java 提供的线程本地变量工具类,它为每个线程提供了一个独立的变量副本。
- 本质:为每个线程分配独立副本,不共享数据
- 用途:解决多线程中共享变量引起的数据混乱
1.ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
2.ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]
3.ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法
4.每一个 ThreadLocal 对象, 只能为当前线程关联一个数据,如果要为当前线程关联多个数 据,就需要使用多个 ThreadLocal 对象实例
5.每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
6.ThreadLocal 中保存数据,在线程销毁后,会自动释放(建议手动释放) 。
🔧 ThreadLocal 常用方法
方法 | 说明 |
---|---|
set(T value) | 设置当前线程的变量值 |
get() | 获取当前线程的变量值 |
remove() | 移除当前线程的变量值,避免内存泄漏(重要) |
🎯 典型用途场景(保存用户登录信息(如当前用户 ID))
public class UserContext {private static final ThreadLocal<Long> userIdHolder = new ThreadLocal<>();public static void setUserId(Long id) {userIdHolder.set(id);}public static Long getUserId() {return userIdHolder.get();}public static void clear() {userIdHolder.remove();}
}
📍通常配合拦截器、过滤器、JWT 使用:登录验证完后存入 ThreadLocal,业务代码就能直接获取当前用户 ID,无需每层传参。
当然也可以保存用户的一些信息
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
2.解析
🎯 1. 作用:线程内共享变量,解决线程安全问题
✅ ThreadLocal 可实现线程内的数据共享,从而避免多线程访问共享数据造成冲突。
🧷 2. 存值方法:set()
给当前线程绑定数据
🧪 可绑定 基本类型 / 对象 / 数组 / Map 等 任意数据
threadLocal.set("userId-123");
🔍 3. 获取方法:get()
相当于线程私有 Map 的 get(key)
🗂️ 每个线程维护一份 ThreadLocalMap,key 是线程,value 是你 set 的值
String value = threadLocal.get();
🧩 4. 一个 ThreadLocal 对象 = 一个数据槽位
📦 想为同一线程绑定多个值?你就要 new 多个
ThreadLocal
实例,例如:
public static ThreadLocal<String> userName = new ThreadLocal<>();
public static ThreadLocal<Long> userId = new ThreadLocal<>();
🧱 5. 建议使用 static
修饰 ThreadLocal 实例
🧷 常作为工具类全局使用,方便访问、只初始化一次
public static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
🧹 6. 自动释放内存(前提:线程终结)
🛡️ 线程销毁后,ThreadLocal 中数据会被自动 GC 清理
❗ 但如果线程池中复用线程,必须手动调用remove()
防止内存泄漏
threadLocal.remove(); // 非常重要
其他常见注意
✅ 数据库多数据源切换
用 ThreadLocal 存放当前线程使用的数据源标识,在 AOP 或拦截器中切换。
✅ 日志 traceId 链路追踪(如打印统一 traceId)
🧱 ThreadLocal 与并发安全
情况 | 是否线程安全 |
---|---|
普通变量 | ❌ 多线程共享,容易数据混乱 |
ThreadLocal | ✅ 每个线程独立副本,互不影响 |
⚠️ 注意事项(新手必须知道)
🔺 一定要手动 remove()!
- 原因:ThreadLocal 引用的是线程私有变量,线程池复用线程时可能出现 内存泄漏
- 解决:
- 处理完必须调用
ThreadLocal.remove()
- 或使用
try-finally
包裹清除逻辑
- 处理完必须调用
try {ThreadLocalHolder.set(value);// 业务处理
} finally {ThreadLocalHolder.remove();
}
📦 延伸阅读
内容 | 推荐方向 |
---|---|
ThreadLocalMap | 底层实现结构(Map) |
InheritableThreadLocal | 子线程继承父线程变量 |
Spring 中的使用 | RequestContextHolder 、SecurityContextHolder |
与线程池结合 | 使用后强制清理,尤其注意 @Async 、CompletableFuture |