ThreadLocal 是不支持继承性的,也就是父线程中设置的参数在子线程中是不能获取到的,所以 InheritedThreadLocal 继承了 ThreadLocal 去实现了这个功能,那么 InheritedThreadLocal 是如何实现这个功能的呢?
下面看一下 InheritedThreadLocal 类的代码
可以看到该类重写了三个方法,可以看到这里的 getMap 和 createMap 方法创建的都是线程中的 inheritableThreadLocals 变量。
可以看到在 Thread 类中是有两个变量的,当我们使用 ThreadLocal 类型的时候使用的就是 threadLocals 变量,当使用 InheritedThreadLocal 类型的时候使用的就是 InheritedThreadLocals 变量。
但是从上面的代码中只是看到换了一个变量而已,看不出来为什么子线程可以获取父线程的变量,这就要看 Thread 类的默认构造方法了。


上面截图中第1步和第2步可能会有点疑惑
- 为什么调用 currentThread() 方法获取的是父线程,不应该是当前线程么?
- 第二步里面的 this 不应该就是表示当前线程么?
不急,我们先看一下InheritedThreadLocal 的使用示例代码吧
package org.example.testthread;/*** @Author 咖啡杯里的茶* @date 2021/8/3*/public class TestThreadLocal {static InheritableThreadLocal<String> inheritedThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {InheritedThreadLocal.set("父线程");// 注意,这里new Thread 就是调用 Thread 类的默认构造方法,而且这里是在 main 线程里面调用的,所以这里面执行的代码在main线程里面执行的new Thread() {@Overridepublic void run() {//inheritedThreadLocal.set("线程1");// 在子线程里面根本就没有设置 inheritedThreadLocal变量的值,但是这里打印出来的结果删除 【子线程:父线程】print("子线程:");}}.start();}static void print(String string) {System.out.println(string + threadLocal.get());}}
其实看完上面的代码注释应该已经明白了,Thread 类的无参构造方法是在 main 线程里面执行的,所以在第一个疑问点(currentThread()方法为什么获取的是父线程)就可以得到解决了。
而第二个疑问点就需要理解 this 这个关键字了,this表示的什么意思?表示当前对象,嗯,是当前对象,而不是当前线程,这里的对象是子线程对象,所以说到这里,第二点应该也明白了。
至此,也就理解了 InheritedThreadLocal 是如何起作用的了。总结起来就两点
- InheritedThreadLoca 继承了 ThreadLocal ,同时重写了 createMap 和 getMap 方法,重写的目的是为了修改使用的变量为 inheritedThreadLocals, 而不是使用 threadLocals
- 在创建子线程对象(这部分代码是在父线程中执行的)的时候,判断父线程的 inheritedThreadLocals 对象是否有值,如果有值就可以给子线程的 inheritedThreadLocals 变量赋值。
为什么ThreadLocal类内部的ThreadLocalMap要用Entry数组实现?
在同一个线程中 set 方法只会设置一个值,那么可以直接使用一个 Entry 来存储不就可以了么?要什么要使用 Entry 数组而且还要考虑扩容问题。
看下面代码
public class ThreadLocalDemo {static ThreadLocal<String> threadLocal;static ThreadLocal<String> threadLocal2;public static void main(String[] args) {threadLocal = new ThreadLocal<>();threadLocal2 = new ThreadLocal<>();threadLocal.set("first");threadLocal2.set("first-1");System.out.println(threadLocal.get());System.out.println(threadLocal2.get());}}
- 其实是因为 ThreadLocal 和 线程之间的关系是多对一的,虽然一个 ThreadLocal 只能存储一个值,但是我们可以在同一个线程中创建多个 ThreadLocal 对象,这多个 ThreadLocal 对象都是对应到这个线程的,所以需要使用数组,而且要考虑扩容。
- ThreadLocal 中的 Entry 数组默认值是 16
Main 线程中 Thread类里面的 createMap 是什么时候调用的
这里只是针对的 main 线程,其他的线程还是如果是第一次设置值还是会调用 createMap 方法,但是 main 线程就没有看到调用 createMap 方法。
