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;

相关推荐
SL_staff26 分钟前
物模型三要素误用避坑指南:属性、事件、服务的正确建模逻辑
java
高频因子挖掘机30 分钟前
股票分红送转后价格“断层”:前复权、后复权和不复权该怎么选?
后端·github·api
一条泥憨鱼30 分钟前
【Redis | 黑马点评】短信登录功能 源码深度分析
java·redis·后端·session·黑马点评
威哥爱编程35 分钟前
【AI全栈12-12】Spring Boot 3.x → 4.x 迁移实操:Jakarta 11、Jackson 3 与 AI 2.0
java·spring boot·后端
威哥爱编程40 分钟前
【AI全栈12-11】Spring Boot 把 AI 接口真正上线扛量:限流 / 降级 / 可观测
java·spring boot·后端
小蒜学长40 分钟前
基于SpringBoot+uni-app的闪洁家政系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·uni-app·家政服务
敲代码的彭于晏1 小时前
2026 K8s 零基础入门:手把手教你一步步搭建 Kubernetes 集群
java·运维·全栈
希洛兔1 小时前
状态模式详解:让对象随内部状态自动改变行为
java·设计模式·状态模式
需要8261 小时前
Spring Boot 启动流程:run() 里到底发生了什么
java·spring boot·后端
云絮.1 小时前
Mybatis(一)
java·数据库·mybatis