java并发:lnheriitableThreadLocal
初识lnheriitableThreadLocal
lnheriitableThreadLocal继承自 ThreadLocal,其提供了一个特性,让子线程可以访问在父线程中设置的本地变量。
思考
在什么情况下子线程需要获取父线程的 threadlocal 变量呢,实现方式有哪些?
最简单的实现方式:创建线程时传入父线程中的变量,将其复制到子线程中
核心应用场景
跨线程日志跟踪
在异步任务系统中,子线程需继承父线程的TraceID以实现全链路日志追踪(如金融交易系统)。
权限上下文传递
后台服务创建子线程时,自动传递当前用户的身份验证信息(如Spring Security的SecurityContext)。
分布式任务分片
当父线程拆解任务到子线程时,需传递分片规则、数据源路由等配置信息。
代码示例
public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(){ @Override protected Integer initialValue() { return 0; } }; threadLocal.set(1); Thread newThread = new Thread() { @Override public void run() { System.out.println(threadLocal.get()); } }; newThread.start(); }
上面这段代码,使得newThread访问到了main方法所在线程中的threadLocal变量的变动,即此处newThread将输出1。
请对比下面这段代码:
public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(){ @Override protected Integer initialValue() { return 0; } }; threadLocal.set(1); Thread newThread = new Thread() { @Override public void run() { System.out.println(threadLocal.get()); } }; newThread.start(); }
此段代码与第一个例子不同的地方在于threadLocal变量的类型不一样,此处newThread将输出0。
底层实现原理
关键数据结构
Thread.inheritableThreadLocals

每个Thread对象持有该字段,存储从父线程继承的InheritableThreadLocal键值对
if (parent.inheritableThreadLocals != null) { this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); }
补充:
ThreadLocalMap
实际存储数据的定制化哈希表,使用弱引用(WeakReference<ThreadLocal<?>>)作为Key避免内存泄漏。
继承机制触发点
InheritableThreadLocal不支持线程池场景,核心原因:线程复用与初始化机制冲突
仅当通过Thread构造函数创建线程时,才会执行init()方法中的继承逻辑。 —— init方法,见JDK8
使用线程池(复用已存在线程)时不会触发继承,这是常见误用点。 —— 创建后不再执行初始化逻辑,导致无法继承当前父线程上下文

源码证据 —— ThreadPoolExecutor执行流程(伪代码)
public void execute(Runnable task) { if (workerCount < corePoolSize) { addWorker(task, true); // 可能创建新线程(此时触发继承) } else { workQueue.offer(task); // 复用现有线程(不触发继承) } }
解释:
仅当线程池扩容时新建线程会触发继承,后续任务复用线程时直接跳过初始化。
源码分析
此处将跟踪Thread的创建过程,以搞明白为什么lnheriitableThreadLocal类型的变量可以暴露在新创建的线程中。
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, target, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. * * @param target * the object whose {@code run} method is invoked when this thread * is started. If {@code null}, this classes {@code run} method does * nothing. */ public Thread(Runnable target) { this(null, target, "Thread-" + nextThreadNum(), 0); }
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { this(group, target, name, stackSize, null, true); }
/** * Initializes a Thread. * * @param g the Thread group * @param target the object whose run() method gets called * @param name the name of the new Thread * @param stackSize the desired stack size for the new thread, or * zero to indicate that this parameter is to be ignored. * @param acc the AccessControlContext to inherit, or * AccessController.getContext() if null * @param inheritThreadLocals if {@code true}, inherit initial values for * inheritable thread-locals from the constructing thread */ private Thread(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); } /* If the security manager doesn't have a strong opinion on the matter, use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } } /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission( SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ this.tid = nextThreadID(); }
上述代码中着色部分是关键,至此将跳转到ThreadLocal中,代码如下:
/** * Factory method to create map of inherited thread locals. * Designed to be called only from Thread constructor. * * @param parentMap the map associated with parent thread * @return a map containing the parent's inheritable bindings */ static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
上述代码调用了ThreadLocalMap的构造函数,代码如下:
/** * Construct a new map including all Inheritable ThreadLocals * from given parent map. Called only by createInheritedMap. * * @param parentMap the map associated with parent thread. */ private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (Entry e : parentTable) { if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }
上述代码中着色部分调用了ThreadLocal中的childValue方法,该方法的代码如下:
/** * 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(); }
上述方法由lnheriitableThreadLocal重写,与此同时,lnheriitableThreadLocal重写了getMap和createMap方法,整体代码如下:
public class InheritableThreadLocal<T> extends ThreadLocal<T> { /** * Computes the child's initial value for this inheritable thread-local * variable as a function of the parent's value at the time the child * thread is created. This method is called from within the parent * thread before the child is started. * <p> * This method merely returns its input argument, and should be overridden * if a different behavior is desired. * * @param parentValue the parent thread's value * @return the child thread's initial value */ protected T childValue(T parentValue) { return parentValue; } /** * Get the map associated with a ThreadLocal. * * @param t the current thread */ ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } /** * Create the map associated with a ThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the table. */ void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
高级应用模式
动态更新传递
重写childValue()方法实现值转换
InheritableThreadLocal<String> local = new InheritableThreadLocal<>() { @Override protected String childValue(String parentValue) { return parentValue + ":child"; } };
注意事项
上下文污染
InheritableThreadLocal<String> context = new InheritableThreadLocal<>(); // 任务1:父线程设置上下文 context.set("Task1-Context"); executor.submit(() -> System.out.println(context.get())); // 输出 Task1-Context // 任务2:新任务未设置上下文 executor.submit(() -> { System.out.println(context.get()); // 仍输出 Task1-Context(错误残留) });
由于线程复用,任务2错误地继承了任务1的旧上下文。
总结
父线程在通过 InheritableThreadLocal类实例的 set 或者 get 方法设置变量时,会创建当前线程的 inheritableThreadLocals变量。
InheritableThreadLocal类通过重写代码,让本地变量保存到了具体线程的 inheritableThreadLocals变量里面。

当父线程创建子线程时,构造函数会把父线程中 inheritableThreadLocals 变量里面的本地变量复制一份保存到子线程的 inheritableThreadLocals 变量里面。
延申阅读
(1)https://github.com/alibaba/transmittable-thread-local

浙公网安备 33010602011771号