TransmittableThreadLocal 问题杂记

news2024/10/7 6:39:36

0、前言

  TransmittableThreadLocal,简称 TTL,是阿里巴巴开源的一个Java库,它能够实现ThreadLocal在多线程间的值传递,适用于使用线程池、异步调用等需要线程切换的场景,解决了ThreadLocal在使用父子线程、线程池时不能正确传递值的问题。
核心实现:捕获(capture)- 重放(replay)- 恢复(restore)

  • 捕获:将父线程的 TTL/ThreadLocal 拷贝一份到子线程中存为快照;
    private static class Snapshot {
        final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value;
        final HashMap<ThreadLocal<Object>, Object> threadLocal2Value;
    }
  • 重放:将快照中的内容存入子线程的 TTL/ThreadLocal 中,并移除不存在快照中的子线程已经存在的 TTL/ThreadLocal;
  • 恢复:清除子线程的 TTL/ThreadLocal。

1、上下文乱象

  背景:为了实现在异步线程中也能正确进行通用字段的填充,引入了 TTL,将原先存储用户上下文信息的 ThreadLocal 换成了 TTL。(注:异步线程通过线程池进行管理)
乱象: 子线程在执行任务的过程中,用户上下文出现了两种状态:run() 执行前后 – 正确信息、run() 执行中 – null,如下图所示。
92f76f1daac51510027eba1a7ca6fe2.png
  代码部分:如下所示。
功能逻辑
4dce615fa1ada1fb99324bad6164d5d.png
线程池装饰器
5a1952f94ac395955a62306e05346ba.png
字段填充
c1759bb945a2cc8c5cc3f7c67d8eb9d.png

2、没有使用 TtlRunnable

  capture,replay,restore 本质是线程任务执行前后的增强方法,这些方法的调用发生于 TtlRunnable 的 run 方法中。

    /**
     * wrap method {@link Runnable#run()}.
     */
    @Override
    public void run() {
        final Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }

        final Object backup = replay(captured);
        try {
            runnable.run();
        } finally {
            restore(backup);
        }
    }

使用方式:

  • 直接调用 TtlRunnable.get(…) 对 Runnable 进行包装增强;
  • 通过 TtlExecutors 工具类获取相应的包装类。

错误例子:
image.png
正确例子:
image.png

3、父子线程引用共享问题

  TTL 默认的上下文复制方式是浅拷贝,这就会造成父子线程中的上下文信息出现共享问题。解决这一问题的方法为:重写 TTL 的 copy 方法,将浅拷贝换成深拷贝。

    /**
     * Computes the value for this transmittable thread-local variable
     * as a function of the source thread's value at the time the task
     * Object is created.
     * <p>
     * This method is called from {@link TtlRunnable} or
     * {@link TtlCallable} when it create, before the task is started.
     * <p>
     * This method merely returns reference of its source thread value(the shadow copy),
     * and should be overridden if a different behavior is desired.
     *
     * @since 1.0.0
     */
    public T copy(T parentValue) {
        return parentValue;
    }

错误例子:

    private final static ThreadLocal<Map<String, Integer>> transmittableThreadLocal = new TransmittableThreadLocal<Map<String, Integer>>() {
        @Override
        protected Map<String, Integer> initialValue() {
            return new HashMap<>();
        }
    };

    private static int i = 0;

    public static void main(String[] args) {
        transmittableThreadLocal.get().put(String.format("key-%d", ++i), i);

        Executor ttlExecutor = TtlExecutors.getTtlExecutor(Executors.newFixedThreadPool(1));
        CompletableFuture.runAsync(()-> {
            try {
                Thread.sleep(3 * 1000);
            } catch (InterruptedException e) {
            }
            System.out.println(StrUtil.format("[{}]子线程:{}", LocalTime.now(), transmittableThreadLocal.get()));
        }, ttlExecutor);

        transmittableThreadLocal.get().put(String.format("key-%d", ++i), i);
        System.out.println(StrUtil.format("[{}]父线程:{}", LocalTime.now(), transmittableThreadLocal.get()));

        transmittableThreadLocal.remove();
    }

image.png
正确例子:

   private final static ThreadLocal<Map<String, Integer>> transmittableThreadLocal = new TransmittableThreadLocal<Map<String, Integer>>() {
        @Override
        protected Map<String, Integer> initialValue() {
            return new HashMap<>();
        }

        @Override
        public Map<String, Integer> copy(Map<String, Integer> parentValue) {
            return parentValue != null ? new HashMap<>(parentValue) : null;
        }
    };

    private static int i = 0;

    public static void main(String[] args) {
        transmittableThreadLocal.get().put(StrUtil.format("key-{}", ++i), i);

        Executor ttlExecutor = TtlExecutors.getTtlExecutor(Executors.newFixedThreadPool(1));
        CompletableFuture.runAsync(()-> {
            try {
                Thread.sleep(3 * 1000);
            } catch (InterruptedException e) {
            }
            System.out.println(StrUtil.format("[{}]子线程:{}", LocalTime.now(), transmittableThreadLocal.get()));
        }, ttlExecutor);

        transmittableThreadLocal.get().put(String.format("key-%d", ++i), i);
        System.out.println(StrUtil.format("[{}]父线程:{}", LocalTime.now(), transmittableThreadLocal.get()));

        transmittableThreadLocal.remove();
    }

image.png


拓展:捕获、重放期间的线程切换和 ThreadLocal 变化。
捕获:
image.png
image.png
重放:

  • 备份

image.png
image.png

  • 重新设置

image.png
image.png


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1560955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SpringBoot从入门到精通】01_SpringBoot概述

一、Spring与SpringBoot 1.1 Spring Spring 是一款目前主流的 Java EE 轻量级开源框架&#xff0c;是 Java 世界最为成功的框架之一。Spring 由“Spring 之父”Rod Johnson(罗宾约翰逊) 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。 广义…

基于ssm的平面设计课程在线学习平台系统(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的平面设计课程在线学习平台系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 前台功能&#xf…

MySql 常用的聚合函数总结

MySQL 中的聚合函数用于对一组数据进行计算&#xff0c;并返回单个值作为结果。以下是常用的 MySQL 聚合函数的总结及其功能描述&#xff1a; 1. COUNT() 功能&#xff1a;用于计算指定列或表中的行数。 语法&#xff1a; COUNT(*) COUNT(expression) 示例&#xff1a; SELECT …

吴恩达深度学习笔记:浅层神经网络(Shallow neural networks)3.9-3.11

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第三周&#xff1a;浅层神经网络(Shallow neural networks)3.9 神 经 网 络 的 梯 度 下 降 &#xff08; Gradient descent for neural networks&#xff09;3.10&#xff08;选修&#xff0…

MATLAB多级分组绘图及图例等细节处理 ; MATLAB画图横轴时间纵轴数值按照不同sensorCode分组画不同sensorCode的曲线

平时研究需要大量的绘图Excel有时候又臃肿且麻烦 尤其是当处理大量数据时可能会拖死Windows 示例代码及数据量展示 因为数据量是万级别的折线图也变成"柱状图"了, 不过还能看出大致趋势! 横轴是时间纵轴是传感器数值图例是传感器所在深度 % data readtable(C:\U…

“美国债务螺旋上升,每百天膨胀万亿”!华尔街:投入比特币是明智之举,美元早晚垮台?

​ 前不久&#xff0c;黄金和比特币价格的双双逼近历史高位&#xff0c;再度吸引了不少金融市场参与者的关注。虽然这两类资产大涨的背后&#xff0c;有着诸如比特币减半临近、地缘局势引发避险等各自的原因&#xff0c;但也有一些业内人士提到了美国政府债务规模激增等无法回…

如何使用 ArcGIS Pro 自动矢量化水系

对于某些要素颜色统一的地图&#xff0c;比如电子地图&#xff0c;可以通过图像识别技术将其自动矢量化&#xff0c;这里为大家介绍一下 ArcGIS Pro 自动矢量化水系的方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的电子地图数据&#…

【C++】list介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. list介绍2. list的构造3. ist iterator的使用4. capacity5. element access6. modifiers7. 迭代器失效8. Operations8.1 reverse8.2 sort8.3 unique8.4 splice 1. list介绍 list是可以在常数范围内在任意位置进行插…

Linux之 线程池 | 单例模式的线程安全问题 | 其他锁

目录 一、线程池 1、线程池 2、线程池代码 3、线程池的应用场景 二、单例模式的线程安全问题 1、线程池的单例模式 2、线程安全问题 三、其他锁 一、线程池 1、线程池 线程池是一种线程使用模式。线程池里面可以维护一些线程。 为什么要有线程池&#xff1f; 因为在…

代码随想录算法训练营第二十五天| 216.组合总和III,17.电话号码的字母组合

题目与题解 216.组合总和III 题目链接&#xff1a;216.组合总和III 代码随想录题解&#xff1a;216.组合总和III 视频讲解&#xff1a;和组合问题有啥区别&#xff1f;回溯算法如何剪枝&#xff1f;| LeetCode&#xff1a;216.组合总和III_哔哩哔哩_bilibili 解题思路&#xf…

超云信创新品发布,引领国产化AI算力新高度

在当前数字化转型的大潮中&#xff0c;算力作为新质生产力的重要动力引擎&#xff0c;对推动经济社会发展起着关键作用。尤其在人工智能领域&#xff0c;随着高性能、安全可控的AI算力需求持续攀升&#xff0c;国产化服务器的研发与应用显得尤为迫切。 作为国内专业的算力基础…

【技巧】如何解除Excel“打开密码”?

给Excel表格设置“打开密码”&#xff0c;可以保护表格不被他人随意打开&#xff0c;那如果后续不需要保护了&#xff0c;不想每次打开Excel都需要输密码&#xff0c;要怎么去除“打开密码”呢&#xff1f; 今天分享3个方法&#xff0c;最后一个方法记得收藏起来&#xff0c;以…

基于springboot实现教师工作量管理系统项目【项目源码+论文说明】

基于springboot实现教师工作量管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了教师工作量管理系统的开发全过程。通过分析教师工作量管理系统管理的不足&#xff0c;创建了一个计算机管理教师工作…

Dragonfly S系列相机 多领域大规模制造应用的首选

Dragonfly S系列工业相机&#xff0c;作为一款模块化紧凑型USB3机器视觉相机&#xff0c;一经推出就广受从生命科学仪器到工厂自动化等行业的青睐。同时它也非常适合各种嵌入式和手持设备的高性能图像采集系统。 Dragonfly S系列相机凭借其模块化、紧凑、轻量级的设计理念&…

在哪买国外服务器便宜?

在哪买国外服务器便宜&#xff1f;在寻找便宜且可靠的国外服务器商家时&#xff0c;我们需要考虑多个因素&#xff0c;包括价格、性能、可靠性、技术支持和扩展性等。下面是一些备受推崇的便宜国外服务器商家。 Amazon Web Services (AWS)。作为全球最大的云服务提供商之一&am…

抖店的运营玩法你真的了解吗?没有货源也能操作的方法来了!

大家好&#xff0c;我是电商小布。 现在越来越多的小伙伴开始察觉到了抖店这个项目的优势&#xff0c;并想要加入进来开一家属于自己的小店。 但是店铺的发展远不是我们看到的那么简单&#xff0c;开店只是我们进入这个市场的第一步。 接下来的运营操作才是关键点。 而这个…

国货护肤更替,“韩束珀莱雅们”会成为常胜将军吗?

【潮汐商业评论/文】 July是一位资深的护肤达人&#xff0c;“这是敏感肌专用的修复水&#xff0c;这是针对熬夜黑眼圈的眼霜&#xff0c;这是军训专用防晒霜&#xff0c;这是……&#xff0c;”虽然粉丝很少&#xff0c;但July总是乐此不疲的分享自己的爱用护肤品。 最近&am…

镭速如何解决UDP传输不通的问题

我们之前有谈到过企业如果遇到UDP传输不通的情况&#xff0c;常见的一些解决方式&#xff0c;同时也介绍了一站式企业文件传输方式-镭速相关优势&#xff0c;如果在实际应用中&#xff0c;若镭速UDP传输出现不通的情况&#xff0c;需要按照网络通信的一般性排查方法以及针对镭速…

linux下使用iperf工具测试通过EC20模组联网后网速

1.我主要参考以下文章资料(现在是VIP文章了):iperf软件编译以及使用_iperf源码编译-CSDN博客 这里我下载的版本为3.1.3&#xff0c;以下附上百度网盘: 链接&#xff1a;https://pan.baidu.com/s/1tPi4oTBUC36jJcM5M5oj_A 提取码&#xff1a;bo4f --来自百度网盘超级会员V6的…

echart 仪表盘实现指针的渐变色及添加图片

需求&#xff1a; 在仪表盘中设置指针为渐变色&#xff0c;并在仪表盘中间添加图片。 实现重点&#xff1a; 1、仪表盘指针渐变色的实现 渐变色通过设置pointer的itemStyle属性内的color实现&#xff0c;重点是echart版本&#xff0c;这个原本使用4.8.0的版本不起作用&#xff…