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

相关推荐
wclass-zhengge6 分钟前
数据结构篇(绪论)
java·数据结构·算法
何事驚慌7 分钟前
2024/10/5 数据结构打卡
java·数据结构·算法
结衣结衣.7 分钟前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
TJKFYY9 分钟前
Java.数据结构.HashSet
java·开发语言·数据结构
kylinxjd10 分钟前
spring boot发送邮件
java·spring boot·后端·发送email邮件
OLDERHARD19 分钟前
Java - MyBatis(上)
java·oracle·mybatis
杨荧20 分钟前
【JAVA开源】基于Vue和SpringBoot的旅游管理系统
java·vue.js·spring boot·spring cloud·开源·旅游
zaim12 小时前
计算机的错误计算(一百一十四)
java·c++·python·rust·go·c·多项式
2401_857439693 小时前
Spring Boot新闻推荐系统:用户体验优化
spring boot·后端·ux
hong_zc3 小时前
算法【Java】—— 二叉树的深搜
java·算法