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;

相关推荐
程序猿乐锅13 分钟前
万字详解Single-flight(单飞模式)
java·开发语言·分布式·后端·学习·single-flight
Java后端的Ai之路24 分钟前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli
AntsData1 小时前
用 12K+ 条亚马逊评论信号,定位机械键盘下一版该改什么
java·大数据·计算机外设
code2cat1 小时前
【随笔】连续批处理:长短请求怎样在迭代之间补位
java·人工智能·后端·大语言模型·vllm
最强小杰1 小时前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
Flynn Liang1 小时前
Spring AI 2.0 深度解析:ChatClient、Advisor 链与 MCP——Java 工程师的 AI 落地最优解
java·人工智能·spring·大模型·架构设计·spring ai
灿飞1 小时前
Kotlin与Java虚拟机六:基础与类型系统:一切皆对象,但对象有代价
java·kotlin
计算机毕设定制辅导-无忧学长2 小时前
《基于微信小程序健康习惯激励系统设计与实现》
java·spring boot·mysql·微信小程序·uniapp·健康习惯激励系统
掘金者阿豪2 小时前
SQL Server数据迁移完成后,凌晨的数据库告警该怎么查?
前端·后端
鶴哥只手遮天2 小时前
AFSIM 2.9.0 深度解析·进阶卷第 1 章 插件源码精读:16 个 WSF 插件的架构与实现
后端