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() {
@Override
public 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 方法。