TransmittableThreadLocal跨线程共享变量

前言

上一篇讲了InheritableThreadLocal在父子线程之间支持上下文环境之间的访问,但是在线程池的场景下实现传递变量值还是不支持的,这种就要用到阿里的开源工具TransmittableThreadLocal,可以在线程池之间传递变量的值,在多线程并发对的场景下,我们一般都会使用线程池来减少创建线程带来的开销问题,需要实现变量之间的传递,就可以使用TransmittableThreadLocal进行存储;

TransmittableThreadLocal使用场景

例如在多线程环境进行登陆,需要获取当前线程变量中的,用户id、用户名称、Token信息等。

typescript 复制代码
//创建TransmittableThreadLocal存储map集合
    private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();


//获取TransmittableThreadLocal里面map值,然后进行赋值
    public static Map<String, Object> getLocalMap()
    {
        Map<String, Object> map = THREAD_LOCAL.get();
        if (map == null)
        {
            map = new ConcurrentHashMap<String, Object>();
            THREAD_LOCAL.set(map);
        }
        return map;
    }

// 获取TransmittableThreadLocal里面变量的值
public static String get(String key)
{
    Map<String, Object> map = getLocalMap();
    return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY));
}

业务中有很多种情况都是涉及到这种场景,通过获取到用户的信息,来获取当前用户下面的一些权限;

csharp 复制代码
/**
 * 获取登录用户信息
 */
public static LoginUser getLoginUser()
{
    return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class);
}

TransmittableThreadLocal源码分析

那么TransmittableThreadLocal是如何在多线程之前实现变量共享的呢,那肯定离不开TtlRunnable简称TTL,先看TransmittableThreadLocal有一个静态的内部类Transmitter,它有这么几个关键的方法,主要就是capture(copy上下文)、replay(存放上下文)、clear(清理上下文)、restore(重置上下文);

less 复制代码
        @NonNull
        public static Object capture() {...}
        @NonNull
        public static Object replay(@NonNull Object captured){... }
        @NonNull
        public static Object clear() {.....}

        public static void restore(@NonNull Object backup) {...}

当然在每个方法里面都会定义一个map包装的Transmittee,Transmittee是个接口分别定义了capture、replay、clear、restore方法;

less 复制代码
final HashMap<Transmittee<Object, Object>, Object> transmittee2Value = newHashMap(transmitteeSet.size());

transmitteeSet的定义我们可以看到是用到了COW(CopyOnWrite)线程安全的并发容器;

typescript 复制代码
private static final Set<Transmittee<Object, Object>> transmitteeSet = new CopyOnWriteArraySet<>();
``

这里面HashMap的key定义可以看到是使用TransmittableThreadLocal,在进行put的操作的时候也用到了和InheritableThreadLocal同样的copyValue方法

在进行代码的追踪可以看到,使用的是SuppliedTransmittableThreadLocal类,定义的TtlCopier接口,调用它的copy方法进行多线程之间的变量copy;

该copy方法也是使用/TtlRunnable/ ,开启一个run方法,最终交给TtlRunnable进行处理;

TtlRunnable的run方法里面有个/replay/方法,返回当前线程的快照信息就是TransmittableThreadLocal里面存储的其他线程的变量信息,finally每次要调用/restore/方法重置当前快照信息,这里又回到上面所说的Transmitter的几个比较关键的方法,最终返回的Snapshot对象就是当前线程ThreadLocal引用;

整体一个流程就是,当前线程TtlRunnable开启一个run方法,先去线程池里去获取上下文信息,然后通过Transmitter实际的执行类,进行copy、replay、restore,存储到transmitteeSet里,通过访问TransmittableThreadLocal.hodler,获取所有的ThreadLocal持有的引用,返回Snapshot引用对象,在传递给TtlRunnable中,完成了一次copy操作;

执行结果验证

通过下面代码, 定义一个全局变量/transmittableThreadLocal/,方法内set进入一个value变量,定义线程池开启两个线程,来获取transmittableThreadLocal的变量,可以看到获取都是同一个transmittableThreadLocal实例变量;

总结

在并发量比较高的场景下,使用线程池的场景下,如果需要实现这种全局的线程变量之间的共享,不妨可以使用下TransmittableThreadLocal;

相关推荐
天天扭码19 分钟前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒25 分钟前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
机器之心1 小时前
全球十亿级轨迹点驱动,首个轨迹基础大模型来了
人工智能·后端
不爱学习的YY酱1 小时前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
丁总学Java1 小时前
Maven项目打包,com.sun.tools.javac.processing
java·maven
kikyo哎哟喂1 小时前
Java 代理模式详解
java·开发语言·代理模式
duration~1 小时前
SpringAOP模拟实现
java·开发语言
小码ssim1 小时前
IDEA使用tips(LTS✍)
java·ide·intellij-idea
潜洋2 小时前
Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序
java·spring boot·后端
暮志未晚Webgl2 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5