ThreadLocal叫做**线程变量**,该变量在每个线程中都创建了一个副本,每个线程都只能访问自己内部的副本变量,即该变量对其他线程而言是隔离的。 源码里是这样注释说明的: ```java /** * 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). * *

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 { //... } ``` ## 简单的例子认识ThreadLocal ```java /** 线程变量 */ private static ThreadLocal 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#set 实现原理 我们来看一下 `threadLocal.set("A")` 实现过程 ```java /** * * 获取当前线程内部的map对象,将值set进去 */ public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的成员变量ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) //如果map不为空,将值设置到map中,key是this,即threadLocal对象 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 是个什么类? ```java /** * 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 类内部 ```java 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 实现原理 ```java ... /** * 初始值null,在ThreadLocal实例化时可以覆盖该方法 */ protected T initialValue() { return null; } ... /** * 从当前线程内部map获取变量值 */ public T get() { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的成员变量ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) { //根据threadLocal对象从map中获取Entry对象 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对象 ThreadLocalMap map = getMap(t); if (map != null) //如果map不为空,将初始值设置到map中,key是this,即threadLocal对象,value是初始值 map.set(this, value); else //如果map为空,则需要创建新的map对象 createMap(t, value); return value; } ``` 非常简单,就是从线程类Thread的成员变量threadLocals中获取变量值。 ## 总结 ThreadLocal 和 Synchronized 都是为了解决多线程中相同变量的访问冲突(线程安全)问题,不同的是: * Synchronized 是通过线程等待,牺牲时间来解决访问冲突 * ThreadLocal 是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于`Synchronized`,ThreadLocal 具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。 正因为 ThreadLocal 的线程隔离特性,使他的应用场景相对来说更为特殊一些。 当某些数据是 **以线程为作用域并且不同线程具有不同的数据副本** 的时候,就可以考虑采用ThreadLocal。 比如:在多数据源切换时,就使用了 ThreadLocal 来设置当前使用的数据源 [191202-Spirng动态多数据源](191202-Spirng动态多数据源.md)