一、前言在上一篇文章中我们已经掌握了 ThreadLocal 的基本使用知道它能为每个线程提供独立的变量副本实现无锁化的线程安全。但你是否有过这样的疑问ThreadLocal 是如何做到线程隔离的线程和 ThreadLocal 之间到底是什么关系本文将带你深入 ThreadLocal 的源码拆解它的底层实现逻辑彻底搞懂线程隔离的核心原理。二、核心思想ThreadLocal 实现线程隔离的核心思想是“数据存储在线程内部”而非 ThreadLocal 自身存储数据。每个 Thread 线程对象中都持有一个 ThreadLocalMap 类型的成员变量当我们通过 ThreadLocal 的 set() 方法存储数据时实际上是将数据存入当前线程的 ThreadLocalMap 中调用 get() 方法时则是从当前线程的 ThreadLocalMap 中取出数据。三、问题本质分析ThreadLocal 的核心问题本质是“如何建立 Thread、ThreadLocal、数据副本三者之间的关联关系”。如果让 ThreadLocal 直接存储所有线程的数据副本会导致 ThreadLocal 持有大量线程的引用容易引发内存泄漏且无法高效区分不同线程的数据。如果让线程直接存储数据又无法与多个 ThreadLocal 变量进行绑定。因此JDK 设计了ThreadLocalMap 作为中间载体让每个线程持有一个 ThreadLocalMap以 ThreadLocal 实例作为 Key以数据副本作为 Value完美解决了三者的关联问题。四、核心逻辑1. Thread 与 ThreadLocalMap 的关联首先我们查看 java.lang.Thread 类的源码会发现它定义了两个与 ThreadLocal 相关的成员变量/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals null;threadLocals存储当前线程的 ThreadLocal 数据副本默认值为 null 由 ThreadLocal 类负责维护。inheritableThreadLocals用于父子线程间的数据传递由 InheritableThreadLocal 类维护。核心结论 每个线程都有自己专属的 ThreadLocalMapThreadLocal 只是一个 “工具类”负责向 Thread 的 ThreadLocalMap 中存取数据。2. ThreadLocal 的 set() 方法核心逻辑ThreadLocal 的 set() 方法是实现数据存储的关键源码如下基于 JDK 8public void set(T value) { // 1. 获取当前线程对象 Thread t Thread.currentThread(); // 2. 获取当前线程的 ThreadLocalMap ThreadLocalMap map getMap(t); // 3. 如果 ThreadLocalMap 已存在直接存入数据否则创建新的 ThreadLocalMap if (map ! null) map.set(this, value); else createMap(t, value); } // 获取当前线程的 ThreadLocalMap ThreadLocalMap getMap(Thread t) { return t.threadLocals; } // 为当前线程创建 ThreadLocalMap并初始化第一个键值对 void createMap(Thread t, T firstValue) { t.threadLocals new ThreadLocalMap(this, firstValue); }逻辑拆解 调用 Thread.currentThread() 获取当前执行的线程对象 t 。通过 getMap(t) 方法拿到线程 t 的 threadLocals 成员变量即 ThreadLocalMap。如果 ThreadLocalMap 已经存在就以 当前 ThreadLocal 实例作为 Key 以要存储的值 value 作为 Value存入 Map 中。如果 ThreadLocalMap 不存在则调用 createMap() 方法为线程 t 创建一个新的 ThreadLocalMap并初始化第一个键值对。3. ThreadLocal 的 get() 方法核心逻辑get() 方法用于从当前线程的 ThreadLocalMap 中获取数据源码如下public T get() { // 1. 获取当前线程对象 Thread t Thread.currentThread(); // 2. 获取当前线程的 ThreadLocalMap ThreadLocalMap map getMap(t); // 3. 如果 ThreadLocalMap 存在则查找对应的 Value if (map ! null) { ThreadLocalMap.Entry e map.getEntry(this); if (e ! null) { SuppressWarnings(unchecked) T result (T)e.value; return result; } } // 4. 如果 ThreadLocalMap 不存在或没有找到对应的 Value则初始化 return setInitialValue(); } // 初始化 ThreadLocal 的值 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; }逻辑拆解 同样先获取当前线程对象和对应的 ThreadLocalMap。如果 ThreadLocalMap 存在则以当前 ThreadLocal 实例为 Key查找对应的 Entry 键值对。如果找到 Entry 则返回对应的 Value否则调用 setInitialValue() 方法。setInitialValue()方法会调用我们重写的 initialValue() 方法获取初始值然后创建 ThreadLocalMap 并存入初始值。五、ThreadLocal 存取数据流程为了让大家更清晰地理解整个过程我们将 ThreadLocal 存取数据的流程拆解为4个步骤步骤 1线程启动ThreadLocalMap 初始化为 null当一个线程比如 Thread-0 启动时它的 threadLocals 成员变量默认是 null 此时 ThreadLocalMap 尚未创建。步骤 2调用 ThreadLocal.set ()触发 ThreadLocalMap 创建当我们在 Thread-0 中调用 threadLocal.set(data) 时获取当前线程 Thread-0 。发现 Thread-0 的 threadLocals 为 null 于是调用 createMap() 方法。createMap()方法会创建一个新的 ThreadLocalMap 对象并将 threadLocal 作为 Key、 data 作为 Value存入这个 Map 中。最后将这个 ThreadLocalMap 对象赋值给 Thread-0 的 threadLocals 成员变量。步骤 3同线程再次调用 set ()直接存入新数据如果在 Thread-0 中再次调用 threadLocal.set(newData) 获取 Thread-0 的 ThreadLocalMap此时已存在。直接以 threadLocal 为 Key将 Value 更新为 newData 。步骤 4调用 ThreadLocal.get ()从当前线程的 Map 中取值当调用 threadLocal.get() 时获取当前线程 Thread-0 的 ThreadLocalMap。以 threadLocal 为 Key 查找对应的 Value返回给调用方。步骤 5线程内其他 ThreadLocal 变量独立存储如果在 Thread-0 中还有另一个 ThreadLocal 变量 threadLocal2 调用 threadLocal2.set(data2) 时同样获取 Thread-0 的 ThreadLocalMap。以 threadLocal2 为 Key、 data2 为 Value存入同一个 ThreadLocalMap 中。此时 Thread-0 的 ThreadLocalMap 中存在两个键值对Key 分别是 threadLocal 和 threadLocal2 彼此互不干扰。六、图解 Thread、ThreadLocal、ThreadLocalMap 的关系为了更直观地理解三者的关联我们用一张图来总结核心结论 每个线程的 ThreadLocalMap 是独立的不同线程的 ThreadLocalMap 互不干扰。同一个线程的 ThreadLocalMap 可以存储多个 ThreadLocal 变量的数据Key 是 ThreadLocal 实例Value 是对应的数据副本。七、总结本文通过源码分析彻底搞懂了 ThreadLocal 的核心原理线程隔离的本质数据存储在 线程自身的 ThreadLocalMap 中而非 ThreadLocal 中。三者关系Thread 持有 ThreadLocalMapThreadLocal 作为 Key数据副本作为 Value。核心方法逻辑set() 和 get() 方法的核心都是 先获取当前线程的 ThreadLocalMap再进行存取操作 。理解了 ThreadLocal 与 Thread 的关联机制后下一个关键问题来了ThreadLocalMap 的底层结构是怎样的它和 HashMap 有什么区别这些问题将在下一篇文章中详细讲解。