世人缺乏的是毅力,而非气力。——雨果
昨天聊了ThreadLocal
可以用作单个线程中变量共享
其底层实现其实就是个Map
,用线程作为key
,不信可以看这部分源码:
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ 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(); }
但是这里有个问题,如果是子线程,就访问不到了
我们深入源码看到这里有这么一个函数
/** * Method childValue is visibly defined in subclass * InheritableThreadLocal, but is internally defined here for the * sake of providing createInheritedMap factory method without * needing to subclass the map class in InheritableThreadLocal. * This technique is preferable to the alternative of embedding * instanceof tests in methods. */ T childValue(T parentValue) { throw new UnsupportedOperationException(); }
该方法在ThreadLocalMap
的构造函数被调用,上面的注释说,该方法明显是在子类InheritableThreadLocal
中定义的,这里提供这个方法,主要是能让Thread
在构造函数中能调用上面说到的InheritableThreadLocal
的childValue
可以看到Thread
的构造函数中:
简单来说,如果是inheritableThreadLocal
,就执行该方法。我们接着往里看ThreadLocalMap
的构造方法
这里把ThreadLocalMap
中所有的元素遍历出来,拿到key
然后执行了childValue
方法
这里key
其实就是我们的子线程
我们再看InheritableThreadLocal
中childValue
的实现
他直接把传入的值return
了出去(绕来绕去的,这里主要是考虑到如果还有别的行为,方便继承后可以拓展)
然后再将子线程作为的key
和父value
组成一个新的Entry
元素,把它放到map
里去
因此它可以在子线程中共享变量,因为它默认的实现就是子线程的key
但是存的父值
写个demo
测一下:
package com.ruben.study; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; /** * ThreadLocal例子 * * @author <achao1441470436@gmail.com> * @since 2021/10/23 16:43 */ public class ThreadLocalDemo { private static final ThreadLocal<Long> MY_LONG_THREAD = new ThreadLocal<>(); private static final ThreadLocal<Long> MY_LONG_INHERITABLE_THREAD = new InheritableThreadLocal<>(); public static void main(String[] args) throws ExecutionException, InterruptedException { MY_LONG_THREAD.set(0L); MY_LONG_INHERITABLE_THREAD.set(1L); // 单线程共享变量 CompletableFuture.runAsync(() -> { // 子线程尝试访问ThreadLocal中的值 System.out.println(MY_LONG_THREAD.get()); System.out.println(MY_LONG_INHERITABLE_THREAD.get()); }).get(); MY_LONG_THREAD.remove(); MY_LONG_INHERITABLE_THREAD.remove(); } }
可以看到同样的代码,上面的ThreadLocal
在子线程中获取不到,而它的实现InheritableThreadLocal
就可以