解决多线程场景下ThreadLocal的变量传递问题

news2024/10/5 13:09:28

问题描述:

ThreadLocal可以用于存储线程独享的变量。可以方便的存储上下文信息,提升代码的简洁性。

然而,ThreadLocal的一个不足之处在于,它不支持在线程嵌套过程中自动地将数据从父线程传递到子线程。这意味着,即使主线程中设置了ThreadLocal变量,子线程默认情况下也无法访问这些变量。

一、引言

ThreadLocal的需求场景即TransmittableThreadLocal的潜在需求场景,如果你的业务需要『在使用线程池等会池化复用线程的执行组件情况下传递ThreadLocal值』则是TransmittableThreadLocal目标场景。

下面是几个典型场景例子。

  1. 分布式跟踪系统 或 全链路压测(即链路打标)
  2. 日志收集记录系统上下文
  3. SessionCache
  4. 应用容器或上层框架跨应用代码给下层SDK传递信息

 详细需求参见文章末尾参考链接

二、线程内数据共享

2.1 ThreadLocal

ThreadLocal可以用于存储线程独享的变量。很容易想到可以使用ThreadLocal用于我们的多语言信息存储。

然而,ThreadLocal的一个不足之处在于,它不支持在线程嵌套过程中自动地将数据从父线程传递到子线程。这意味着,即使主线程中设置了ThreadLocal变量,子线程默认情况下也无法访问这些变量。

demo

public class TlTest {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executorService = Executors.newFixedThreadPool(1);

        ThreadLocal<String> threadLocal = new ThreadLocal<String>();
        threadLocal.set("main thread set value");

        executorService.submit(() -> {
            String value = threadLocal.get();
            System.out.printf("child threadA, value is : %s%n", value);
        });

        executorService.submit(() -> {
            String value = threadLocal.get();
            System.out.printf("child threadB, value is : %s%n", value);
        });
        
        Thread.sleep(1000);

        System.out.printf("main thread, value is : %s%n", threadLocal.get());

    }

}


运行结果:只有主线程中可以获取到数据,子线程无法获取到数据





2.2 InheritableThreadLocal

Java提供了InheritableThreadLocal,允许子线程继承并使用父线程中的线程本地变量。但是,当InheritableThreadLocal应用于线程池时,它可能会引发问题。线程池中的核心线程通常不会在每次任务完成后立即被回收,这意味着存储在InheritableThreadLocal中的数据也不会被清除。因此,当线程被复用后,执行后续任务时,新任务可能会读取到旧任务留下的脏数据。

demo:

public class ITlTest {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executorService = Executors.newFixedThreadPool(1);

        InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
        threadLocal.set("main thread set value");

        executorService.submit(() -> {
            String value = threadLocal.get();
            System.out.printf("child threadA, lang is : %s%n", value);
            threadLocal.set("child threadA set Value");
        });

        executorService.submit(() -> {
            String value = threadLocal.get();
            System.out.printf("child threadB, lang is : %s%n", value);
            threadLocal.set("child threadB set Value");
        });

        Thread.sleep(1000);

        System.out.printf("main thread, value is : %s%n", threadLocal.get());

    }

}


运行结果:子线程B获得到了子线程A中设置的脏数据。







2.3 TransmittableThreadLocal

TransmittableThreadLocal(TTL)是对TransmittableThreadLocal的一个改进,它解决了线程池等池化场景中的数据传递问题。通过提供一种机制来传递ThreadLocal值,TTL确保了在异步执行和上下文传递数据时的准确性。这使得线程池中的线程能够在执行任务时正确地接收和传递数据,避免了因数据不一致性而可能导致的问题。

demo:

public class TtdlTest {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService = TtlExecutors.getTtlExecutorService(executorService);

        TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<String>();
        threadLocal.set("main thread set value");

        executorService.submit(() -> {
            String value = threadLocal.get();
            System.out.printf("child threadA, lang is : %s%n", value);
            threadLocal.set("child threadA set Value");
        });

        executorService.submit(() -> {
            String value = threadLocal.get();
            System.out.printf("child threadB, lang is : %s%n", value);
            threadLocal.set("child threadB set Value");
        });

        Thread.sleep(1000);

        System.out.printf("main thread, value is : %s%n", threadLocal.get());


    }

}


运行结果:主线程、子线程A、子线程B均可以正确的读取到主线程中设置的数据







三、结论

TransmittableThreadLocal提供了用于在多线程和异步执行场景中传递上下文数据的解决方案。通过使用TTDL,可以避免线程安全问题,减少线程池中的数据不一致问题,简化代码逻辑,提高应用程序的国际化和可维护性。

👉 TransmittableThreadLocal(TTL):在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。一个Java标准库本应为框架/中间件设施开发提供的标配能力,本库功能聚焦 & 0依赖,支持Java 6~21

JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时ThreadLocal值传递到 任务执行时

本库提供的TransmittableThreadLocal类继承并加强InheritableThreadLocal类,解决上述的问题,使用详见 User Guide。

整个TransmittableThreadLocal库的核心功能(用户API、线程池ExecutorService/ForkJoinPool/TimerTask及其线程工厂的Wrapper;开发者API、框架/中间件的集成API),只有 ~1000 SLOC代码行,非常精小。



参考资料

•TransmittableThreadLocal官方GitHub仓库:GitHub - alibaba/transmittable-thread-local: 📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.

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

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

相关文章

面试笔记——框架篇Spring系列(Spring、SpringMVC、SpringBoot)

Spring 线程安全 singleton : bean在每个Spring IOC容器中只有一个实例。 prototype&#xff1a;一个bean的定义可以有多个实例。 问题一&#xff1a; Spring中的单例bean是否是线程安全的&#xff1f; ControllerRequestMapping("/user")public class UserContro…

Springboot+vue的旅游信息推荐系统设计与实现+数据库+论文+数据库表结构文档+免费远程调试

项目介绍: Springbootvue的旅游信息推荐系统设计与实现。Javaee项目&#xff0c;springboot vue前后端分离项目 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringBoot Mybati…

为何ChatGPT日耗电超50万度?

看新闻说&#xff0c;ChatGPT每天的耗电量是50万度&#xff0c;国内每个家庭日均的耗电量不到10度&#xff0c;ChatGPT耗电相当于国内5万个家庭用量。 网上流传&#xff0c;英伟达创始人黄仁勋说&#xff1a;“AI的尽头是光伏和储能”&#xff0c;大佬的眼光就是毒辣&#xff…

【云能耗管理系统在某大型商场的应用】安科瑞Acrel-EIOT能源物联网平台方案

摘要&#xff1a;依据对上海市某大型商场现场考察的结果&#xff0c;提出通过建设云能耗管理系统的方案来改善商场能耗的管理现状。首先充分搜集建筑信息和设备运行工况&#xff0c;合理设计系统实施方案&#xff0c;解决现场数据采集和传输障碍&#xff0c;完成云能耗管理系统…

Python 全栈体系【四阶】(十九)

第五章 深度学习 一、基本理论 4. 神经网络的改进 4.3 循环神经网络 4.3.1 标准 CNN 模型的不足 假设数据之间是独立的。标准 CNN 假设数据之间是独立的&#xff0c;所以在处理前后依赖、序列问题&#xff08;如语音、文本、视频&#xff09;时就显得力不从心。这一类数据…

解决 cv2.imread读取带中文路径图片问题

http://t.csdnimg.cn/i8CXn 1.问题&#xff1a; # 中草药数据集样本可视化展示 import cv2 import matplotlib.pyplot as plt %matplotlib inline plt.title("heshouwu") plt.imshow(cv2.imread(r"D:\home\aistudio\data1\archive\train\何首乌\heshouwu_0001.…

各大pdf转word软件都用的哪家的ocr引擎?

国内一般的PDF软件一般都调用某国际PDF原厂的OCR接口&#xff0c;但这家公司是主要做PDF&#xff0c;在OCR方面并不专注&#xff0c;一些不是很复杂的场景还能应付得过来&#xff0c;复杂一点的效果就强差人意了&#xff0c;推荐用金鸣表格文字识别系统&#xff0c;它主要有以下…

位段详细解释

结构体位段的使用原则 在C语言中&#xff0c;结构体&#xff08;Struct&#xff09;是一种复合数据类型&#xff0c;它允许我们将多个不同类型的数据项组合成一个单一的实体。位段&#xff08;Bit Field&#xff09;是结构体中的一个特殊成员&#xff0c;它允许我们只取结构体…

专注无线MCU:STM32WL33CCV6A、STM32WL33CCV7A、STM32WL33K8V7TR、STM32WL33KBV7TR设计用于RF无线应用

一、STM32WL33CC &#xff1a;Sub-GHz无线微控制器&#xff0c;单核Arm Cortex-M0 概述 STM32WL33xx是一款高性能超低功耗无线应用处理器&#xff0c;用于1 GHz以下频段的RF无线应用。它设计用于在免许可ISM和SRD频段&#xff08;如433、868和915 MHz&#xff09;下工作。 …

Apache Spark

一、Apache Spark 1、Spark简介 Apache Spark是用于大规模数据 (large-scala data) 处理的统一 (unified) 分析引擎。 Spark官网 Spark最早源于一篇论文Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing,该论文是由加州大学柏…

网络安全-文件包含

一、php://input 我们先来看一个简单的代码 <meta charset"utf8"> <?php error_reporting(0); $file $_GET["file"]; if(stristr($file,"php://filter") || stristr($file,"zip://") || stristr($file,"phar://&quo…

Python---常用的web框架

目录 Django创建Django项目启动Django项目引入APP视图函数例如纯文本JSON格式数据重定向渲染页面返回错误提示 FlaskPyramidTornado Django 特点&#xff1a;Django是一个全功能的Web框架&#xff0c;提供了许多内置的功能和工具&#xff0c;如ORM、表单处理、认证等。它的设计…

AWS EC2 学习之: 使用 PuTTY 从 Windows 连接到 Linux 实例

启动您的实例之后&#xff0c;您可以连接到该实例&#xff0c;然后像使用您面前的计算机一样来使用它。 注意 启动实例后&#xff0c;需要几分钟准备好实例&#xff0c;以便您能连接到实例。检查您的实例是否通过了状态检查。您可以在 Instances 页上的 Status Checks 列中查…

Python基础学习笔记(二)

Python基础语法 注释 注释有: 单行注释、多行注释、文档注释。 单行注释采用 # 符号,后面跟随的都是注释内容多行注释采用 (三个单引号) 或者 """(三个双引号) 包围文档注释采用 """ 包围&#xff0c;一般出现在模块&#xff0c;类&#xff0c;…

AI:Nvidia官网人工智能大模型工具合集(文本生成/图像生成/视频生成)的简介、使用方法、案例应用之详细攻略

AI&#xff1a;Nvidia官网人工智能大模型工具合集(文本生成/图像生成/视频生成)的简介、使用方法、案例应用之详细攻略 目录 Nvidia官网人工智能大模型工具合集的简介 1、网站主要功能包括: Nvidia官网人工智能大模型工具合集的使用方法 1、SDXL-Turbo的使用 2、GEMMA-7B的…

MySQL三种开窗函数详细用法,图文详解

开窗函数的详细用法 第一章、开窗函数的语法1.1&#xff09;从聚合开窗函数讲起1.2&#xff09;开窗函数之取值1.3&#xff09;排名开窗函数 第一章、开窗函数的语法 开窗函数的语法为&#xff1a;over(partition by 列名1 order by 列名2 )&#xff0c;括号中的两个关键词par…

MP4如何把视频转MOV格式? MP4视频转MOV格式的技巧

在现代的数字媒体时代&#xff0c;视频格式转换成为了许多用户必须掌握的技能。特别是将MP4视频转换为MOV格式&#xff0c;这对于需要在Apple设备上播放或编辑视频的用户来说尤为重要。本文将详细介绍如何将MP4视频转换为MOV格式&#xff0c;帮助读者轻松应对不同设备和平台的需…

JavaEE企业开发新技术4

2.16 模拟Spring IOC容器功能-1 2.17 模拟Spring IOC容器功能-2 什么是IOC&#xff1f; 控制反转&#xff0c;把对象创建和对象之间的调用过程交给Spring框架进行管理使用IOC的目的&#xff1a;为了耦合度降低 解释&#xff1a; 模仿 IOC容器的功能&#xff0c;我们利用 Map…

Android内存优化项目经验分享 兼顾效率与性能

背景 项目上线一段时间后,回顾重要页面 保证更好用户体验及生产效率&#xff0c;做了内存优化和下载导出优化&#xff0c;具体效果如最后的一节的表格所示。 下面针对拍摄流程的两个页面 预览页 导出页优化实例进行介绍&#xff1a; 一.拍摄前预览页面优化 预览效果问题 存在…

2024-简单点-pandas

pandas pandas to numpy 尽量不用.values提取数据 numexpr 和 bottleneck加速 布尔操作 describe 自定义describe .pipe df.apply 行或者列级别函数级别应用