ThreadLocal 详解
ThreadLocal对外提供;的API如下:
- public T get()
从线程上下文环境中获取设置的值。
- public void set(T value)
将值存储到线程上下文环境中,供后续使用。
- public void remove()
清除线程本地上下文环境。
上述API使用简单,关键是要理解 ThreadLocal 的内部存储结果。
ThreadLocal存储结构
上图的几个关键点如下:
- 数据存储位置
当线程调用 threadLocal 对象的 set(Object value) 方法时,数据并不是存储在 ThreadLocal 对象中,而是存储在 Thread 对象中,这也是 ThreadLocal 的由来,具体存储在线程对象的threadLocals 属性中,其类型为 ThreadLocal.ThreadLocalMap。
- ThreadLocal.ThreadLocalMap
Map 结构,即键值对,键为 threadLocal 对象,值为需要存储到线程上下文的值(threadLocal#set)方法的参数。
源码分析 ThreadLocal get方法
代码@1:获取当前线程。
代码@2:获取线程的 threadLocals 属性,在上图中已展示其存储结构。
代码@3:如果线程对象的 threadLocals 属性不为空,则从该 Map 结构中,用 threadLocal 对象为键去查找值,如果能找到,则返回其 value 值,否则执行代码@4。
代码@4:如果线程对象的 threadLocals 属性为空,或未从 threadLocals 中找到对应的键值对,则调用该方法执行初始化
代码@1:调用 initialValue() 获取默认初始化值,该方法默认返回 null,子类可以重写,实现线程本地变量的初始化。
代码@2:获取当前线程。
代码@3:获取该线程对象的 threadLocals 属性。
代码@4:如果不为空,则将 threadLocal:value 存入线程对象的 threadLocals 属性中。
代码@5:否则初始化线程对象的 threadLocals,然后将 threadLocal:value 键值对存入线程对象的threadLocals 属性中。
源码分析 ThreadLocal set方法
在掌握了 get 方法实现细节,set 方法、remove 其实现的逻辑基本一样,就是对线程对象的threadLocals 属性进行操作( Map结构)。
ThreadLocal局限性
运行结果如下:
从结果上来看,在子线程中无法访问在父线程中设置的本地线程变量,那我们该如何来解决该问题呢?
为了解决该问题,JDK引入了另外一个线程本地变量实现类 InheritableThreadLocal,接下来将重点介绍 InheritableThreadLocal 的实现原理。