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

image.png
image.png
上面截图中第1步和第2步可能会有点疑惑

  1. 为什么调用 currentThread() 方法获取的是父线程,不应该是当前线程么?
  2. 第二步里面的 this 不应该就是表示当前线程么?

不急,我们先看一下InheritedThreadLocal 的使用示例代码吧

  1. package org.example.testthread;
  2. /**
  3. * @Author 咖啡杯里的茶
  4. * @date 2021/8/3
  5. */
  6. public class TestThreadLocal {
  7. static InheritableThreadLocal<String> inheritedThreadLocal = new InheritableThreadLocal<>();
  8. public static void main(String[] args) {
  9. InheritedThreadLocal.set("父线程");
  10. // 注意,这里new Thread 就是调用 Thread 类的默认构造方法,而且这里是在 main 线程里面调用的,所以这里面执行的代码在main线程里面执行的
  11. new Thread() {
  12. @Override
  13. public void run() {
  14. //inheritedThreadLocal.set("线程1");
  15. // 在子线程里面根本就没有设置 inheritedThreadLocal变量的值,但是这里打印出来的结果删除 【子线程:父线程】
  16. print("子线程:");
  17. }
  18. }.start();
  19. }
  20. static void print(String string) {
  21. System.out.println(string + threadLocal.get());
  22. }
  23. }

其实看完上面的代码注释应该已经明白了,Thread 类的无参构造方法是在 main 线程里面执行的,所以在第一个疑问点(currentThread()方法为什么获取的是父线程)就可以得到解决了。
而第二个疑问点就需要理解 this 这个关键字了,this表示的什么意思?表示当前对象,嗯,是当前对象,而不是当前线程,这里的对象是子线程对象,所以说到这里,第二点应该也明白了。
至此,也就理解了 InheritedThreadLocal 是如何起作用的了。总结起来就两点

  1. InheritedThreadLoca 继承了 ThreadLocal ,同时重写了 createMap 和 getMap 方法,重写的目的是为了修改使用的变量为 inheritedThreadLocals, 而不是使用 threadLocals
  2. 在创建子线程对象(这部分代码是在父线程中执行的)的时候,判断父线程的 inheritedThreadLocals 对象是否有值,如果有值就可以给子线程的 inheritedThreadLocals 变量赋值。

为什么ThreadLocal类内部的ThreadLocalMap要用Entry数组实现?

在同一个线程中 set 方法只会设置一个值,那么可以直接使用一个 Entry 来存储不就可以了么?要什么要使用 Entry 数组而且还要考虑扩容问题。
看下面代码

  1. public class ThreadLocalDemo {
  2. static ThreadLocal<String> threadLocal;
  3. static ThreadLocal<String> threadLocal2;
  4. public static void main(String[] args) {
  5. threadLocal = new ThreadLocal<>();
  6. threadLocal2 = new ThreadLocal<>();
  7. threadLocal.set("first");
  8. threadLocal2.set("first-1");
  9. System.out.println(threadLocal.get());
  10. System.out.println(threadLocal2.get());
  11. }
  12. }
  • 其实是因为 ThreadLocal 和 线程之间的关系是多对一的,虽然一个 ThreadLocal 只能存储一个值,但是我们可以在同一个线程中创建多个 ThreadLocal 对象,这多个 ThreadLocal 对象都是对应到这个线程的,所以需要使用数组,而且要考虑扩容。
  • ThreadLocal 中的 Entry 数组默认值是 16

Main 线程中 Thread类里面的 createMap 是什么时候调用的

这里只是针对的 main 线程,其他的线程还是如果是第一次设置值还是会调用 createMap 方法,但是 main 线程就没有看到调用 createMap 方法。