一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
每天一题,一起进大厂,欢迎大家点击阅读。《一起进大厂系列》是我最近刚开始准备的,主要是针对大厂面试题进入针对性深入学习。欢迎大家关注该专栏,会进行不间断更新。一起加油,一起进大厂。话不多说,进入今天的正题。
去年我专门针对ThreadLocal讲过几个使用场景案例,不了解的可以去看下。由于我开始攻克面试题,所以我们今天来一起针对性的学习下
温故知新
先来简单回顾下ThreadLoca:
ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
那么就引起今天的面试题了,既然是多线程,为每个线程创建一个副本。请问子线程如何获取父线程的值?。
还记得我去年写的文章最后写到:
一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法
是的,今天我们对ThreadLocalMap好好研究下:
摸着测试例子过河
public static void main(String[] args) throws InterruptedException {
//父线程
Thread parentParent = new Thread(() -> {
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
threadLocal.set(1);
//这个是关键
InheritableThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<>();
inheritableThreadLocal.set(2);
//模拟子线程去获取父线程的值
new Thread(() -> {
//同样都是子线程获取父线程,两者究竟能获取到什么,可以猜下
System.out.println("threadLocal=" + threadLocal.get());
System.out.println("inheritableThreadLocal=" + inheritableThreadLocal.get());
}).start();
}, "父线程");
//启动父线程
parentParent.start();
}
我们运行结果下:
threadLocal=null
inheritableThreadLocal=2
不言而喻,inheritableThreadLocal拿到了父线程的值。
其实原理很简单 我们接着往下看。
看源码
先看Thread: 内部维护了两个ThreadLocalMap
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
接着往下看,其构造方法调用了init方法, init方法把inheritThreadLocals值设置为了true
public Thread() {
//调用init
init(null, null, "Thread-" + nextThreadNum(), 0);
}
这里之所以放成图片 是为了让大家好看到这个提示inheritThreadLocals 为true
接着跟下去。
可以看到 当inheritThreadLocals的值为true并且其父线程的inheritableThreadLocals不为null时, 把其父线程inheritableThreadLocals 赋值给当前线程的inheritableThreadLocals
这就是子线程可以获取到父线程ThreadLocal值的关键。
发现关键信息,回头再看源码
我们回过头来再看InheritableThreadLocal的 get方法,其实就是ThreadLocal的get()方法。
public T get() {
Thread t = Thread.currentThread();
//重写
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
这里注意:InheritableThreadLocal 对ThreadLocal 的getMap()方法进行重写
我们对上述图中方法做下解释
ThreadLocalMap getMap(Thread t) {
//获取线程自己的变量threadLocals,并绑定到当前调用线程的成员变量threadLocals上
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
//创建给ThreadLocalMap的table属性赋值,并且将firstValue放在数组首位。
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
看到这里明白了吧!!!
总结
-
InheritableThreadLocal类继承了ThreadLocal类,并重写了childValue、getMap、createMap方法。 -
createMap方法不仅创建了threadLocals,同时也将要添加的本地变量值添加到了threadLocals中。 -
其中createMap方法在被调用的时候,创建的是inheritableThreadLocal而不是threadLocals。 -
那么同理,getMap方法在当前调用者线程调用get方法的时候返回的也不是threadLocals而是inheritableThreadLocal。
OK,今天我们就学习到这里,一起加油,一起坚持,一起进大厂!!!
啰嗦一句,欢迎大家学习一起进大厂系列