InheritableThreadLocal是如何实现变量线程共享的

前言

先说ThreadLocal吧,最早使用的场景是将用户信息存储在ThreadLocal里面,如果是同一个线程进来就不用重新获取用户信息了,直接从本地线程变量里面获取,在业务的很多地方都需要使用用户信息,存储到ThreadLocal里方便调用,每个线程持有一个ThreadLocalMap对象,Map的key是Threadlocal实例本身,value是threadLocals真正存储的值;

ThreadLocal

ThreadLocal主要作用

  1. 线程间的数据隔离
  2. 存储事务信息
  3. Session会话管理
csharp 复制代码
private  final static ThreadLocal<String> threadLocal =new ThreadLocal<>();

public static void main(String[] args) {
    threadLocal.set("parent thread data");
    System.out.println(Thread.currentThread().getName()+"ThreadLocal data:" +threadLocal.get());
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" data:" +threadLocal.get());
        }
    }).start();

}

子线程无法获取主线程ThreadLocal存的变量

InheritableThreadLocal

如果我们把ThreadLocal换成InheritableThreadLocal

csharp 复制代码
private  final static InheritableThreadLocal<String> threadLocal =new InheritableThreadLocal<>();

public static void main(String[] args) {
    threadLocal.set("parent thread data");
    System.out.println(Thread.currentThread().getName()+"ThreadLocal data:" +threadLocal.get());
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" data:" +threadLocal.get());
        }
    }).start();

}

可见子线程就可以访问主线程的变量了

InheritableThreadLocal重写方法

InheritableThreadLocal继承了ThreadLocal方法,并且重写了getMap和 createMap方法

scala 复制代码
public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    //该方法主要是父线程向子线程复制InheritableThreadLocal变量
    protected T childValue(T parentValue) {
        return parentValue;
    }

    //重写了ThreadLocal类,现在getMap指向的引用不再是theadlocals而是inheritableThreadLocals
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    //创建ThreadLocalMap,指向inheritableThreadLocals
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

主线程向子线程传递数据

要从Thread类看起,Thread里面有两个变量

Thread的init初始化方法

csharp 复制代码
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

关键代码,inheritThreadLocals和parent的inheritThreadLocals不为空,这里的parent指的就是主线程,将父线程传递给子线程

createInheritedMap方法

接着来看 createInheritedMap方法,返回了一个ThreadLocalMap;

javascript 复制代码
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}

ThreadLocalMap主要是parentMap的变量逐一复制到子线程

ini 复制代码
        private ThreadLocalMap(ThreadLocalMap parentMap) {
        //父线程ThreadLocalMap,获取ThreadLocal存在方式为entry数组
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                      //这里调用了childValue方法,父线程向子线程复制ThreadLocal变量的方法
                        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++;
                    }
                }
            }
        }

InheritableThreadLocal支持线程池之间的传递么?

通过线程之前的变量传递,可以通过InheritableThreadLocal解决,那么开启线程池,在线程池中进行变量的传递共享可以么?

使用单例的线程池,模拟父子线程之前的传递;

ini 复制代码
ExecutorService executorService = Executors.newSingleThreadExecutor();
InheritableThreadLocal<String> threadLocal =new InheritableThreadLocal<>();
for (int i = 0; i <4 ; i++) {
    threadLocal.set("parent thread data -"+i);
    Thread.sleep(3000);
    CompletableFuture.runAsync(() -> System.out.println(threadLocal.get()), executorService);

}
executorService.shutdownNow();

执行结果,可以看出是如何完成变量共享的,那么如何才能在线程池内完成线程共享呢,下一篇会讲到;

总结

至此就可以得出结论,使用InheritableThreadLocal存储变量,有一步的操作就是将父线程里存储的变量,通过entry数组的方式循环进行传递,这样就实现了线程之前数据的可见性。

相关推荐
leobertlan3 小时前
2025年终总结
前端·后端·程序员
面向Google编程3 小时前
从零学习Kafka:数据存储
后端·kafka
易安说AI4 小时前
Claude Opus 4.6 凌晨发布,我体验了一整晚,说说真实感受。
后端
易安说AI4 小时前
Ralph Loop 让Claude无止尽干活的牛马...
前端·后端
易安说AI4 小时前
用 Claude Code 远程分析生产日志,追踪 Claude Max 账户被封原因
后端
JH30734 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
颜酱5 小时前
图结构完全解析:从基础概念到遍历实现
javascript·后端·算法
Coder_Boy_5 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble6 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟6 小时前
使用ASM和agent监控属性变化
java