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;

相关推荐
Harvil_2 分钟前
任务跑一半,API 突然报“对话超长“断流:我给 Agent 修的紧急逃生通道
后端
用户EasyAdminBlazor4 分钟前
EasyAdminBlazor SignalR 实时消息源码解析:从 NotificationHub 到站内消息
后端
程序猿乐锅11 分钟前
【黑马点评 | 第十一篇】关注 Feed 流实现
java·数据库·redis·分布式·后端·缓存·maven
用户EasyAdminBlazor13 分钟前
EasyAdminBlazor 定时任务:FreeScheduler 可视化调度与任务管理
后端
开开心心就好23 分钟前
图片白底怎么去掉?抠图工具抠完背景透明
java·服务器·开发语言·pdf·ocr·散列表·启发式算法
Cosolar34 分钟前
云端部署阿里 Qwen-Image-2.1 保姆级教程
人工智能·后端·github
东方芷兰43 分钟前
Agent 技术摘要 05 —— BP、SaaS、B2B、C2B、B2C
java·笔记·python·语言模型·自然语言处理
Chen—LSN1 小时前
数据结构——拿捏复杂度
java·c语言·开发语言·数据结构·经验分享·笔记·算法
一条破秋裤1 小时前
Linux 线程同步:条件变量
java·linux·jvm
步行cgn1 小时前
Spring AOP 切面的先后顺序详解
java·spring