ThreadLocal 理解
ThreadLocal叫做线程变量,该变量在每个线程中都创建了一个副本,每个线程都只能访问自己内部的副本变量,即该变量对其他线程而言是隔离的。
源码里是这样注释说明的:
/** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * {@code get} or {@code set} method) has its own, independently initialized * copy of the variable. {@code ThreadLocal} instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). * * <p>For example, the class below generates unique identifiers local to each * thread. * A thread's id is assigned the first time it invokes {@code ThreadId.get()} * and remains unchanged on subsequent calls. * * ... * * @author Josh Bloch and Doug Lea * @since 1.2 */ public class ThreadLocal<T> { //... }
简单的例子认识ThreadLocal
/** 线程变量 */ private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { new Thread(() -> { threadLocal.set("A"); System.out.println("线程A的线程变量值:" + threadLocal.get()); }).start(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程B的线程变量值(before set):" + threadLocal.get()); threadLocal.set("B"); System.out.println("线程B的线程变量值(after set):" + threadLocal.get()); }).start(); }
输出结果:
线程A的线程变量值:A 线程B的线程变量值(before set):null 线程B的线程变量值(after set):B
可见线程A和线程B并不共享线程变量 threadLocal
ThreadLocal#set 实现原理
我们来看一下 threadLocal.set("A")
实现过程
/** * * 获取当前线程内部的map对象,将值set进去 */ public void set(T value) { // 获取当前线程 Thread t = Thread.currentThread(); // 获取线程内部 ThreadLocalMap 对象 ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else// 给线程threadLocals变量new一个ThreadLocalMap对象,并添加第一个键值对 createMap(t, value); } ...... ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * 创建一个ThreadLocalMap对象覆盖掉线程内部已有的threadLocals,并设置值 */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
很明显,threadLocal.set("A")
实际上就是向当前线程内部变量 threadLocals 设置值
顺藤摸瓜我们再来看看 ThreadLocalMap 是个什么类?
/** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { .... }
原来ThreadLocalMap 是 ThreadLocal 的内部类,是一个自定义 hash map,只适用于线程局部变量。可以把它就看做是一个 HashMap。
顺藤又摸一个瓜,看下 t.threadLocals
是什么鬼?这次是在 Thread 类内部
public class Thread implements Runnable { ... // line 179 /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; ... }
threadLocals 只是线程类Thread的一个成员变量,默认null
ThreadLocal#get 实现原理
... /** * 初始值,在ThreadLocal实例化时可以覆盖该方法 */ protected T initialValue() { return null; } ... /** * 从当前线程内部map获取变量值 */ 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(); } /** * 设置初始值,如果当前线程threadLocals为空,则创建一个ThreadLocalMap实例 */ private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
非常简单,就是从线程类Thread的成员变量threadLocals中获取变量值。
总结
ThreadLocal 和 Synchronized 都是为了解决多线程中相同变量的访问冲突问题,不同的是:
- Synchronized 是通过线程等待,牺牲时间来解决访问冲突
- ThreadLocal 是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于
Synchronized
,ThreadLocal 具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
正因为 ThreadLocal 的线程隔离特性,使他的应用场景相对来说更为特殊一些。
当某些数据是 以线程为作用域并且不同线程具有不同的数据副本 的时候,就可以考虑采用ThreadLocal。
比如:在多数据源切换时,就使用了 ThreadLocal 来设置当前使用的数据源 Spring动态多数据源
分类:
Java
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律