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数组的方式循环进行传递,这样就实现了线程之前数据的可见性。

相关推荐
开开心心就好3 分钟前
系统管理工具,多功能隐私清理文件粉碎工具
java·网络·windows·r语言·电脑·excel·symfony
小杨同学497 分钟前
C 语言贪心算法实战:解决经典活动选择问题
后端
+VX:Fegn08958 分钟前
计算机毕业设计|基于springboot + vue物流配送中心信息化管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·小程序·课程设计
随丶芯9 分钟前
IDEA安装leetcode-editor插件
java·开发语言
范什么特西14 分钟前
下载idea旧版本
java·ide·intellij-idea
计算机毕设指导620 分钟前
基于微信小程序的钓鱼论坛系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
qq_124987075321 分钟前
基于微信小程序的宠物交易平台的设计与实现(源码+论文+部署+安装)
java·spring boot·后端·微信小程序·小程序·毕业设计·计算机毕业设计
小毅&Nora22 分钟前
【Java线程安全实战】⑧ 阶段同步的艺术:Phaser 与 Condition 的高阶玩法
java·多线程
内存不泄露23 分钟前
基于Spring Boot和Vue的企业办公自动化系统设计与实现
java·vue.js·spring boot·intellij-idea
禹曦a24 分钟前
Java实战:Spring Boot 构建电商订单管理系统RESTful API
java·开发语言·spring boot·后端·restful