线程本地变量交换框架-TransmitterableThreadLocal(阿里开源)

news2024/9/17 8:42:32

上文 :秒级达百万高并发框架-Disruptor


TransmitterableThreadLocal介绍

    TransmitterableThreadLocal简称TTL 是阿里巴巴开源的一个框架。TransmittableThreadLocal是对Java中的ThreadLocal进行了增强和扩展。它旨在解决在线程池或异步任务调用链中,ThreadLocal无法正确传递值的问题。

TransmitterableThreadLocal源码:https://github.com/alibaba/transmittable-thread-local

754a9076972d6129c7fc57468b2aaacb.png

TransmitterableThreadLocal解决了什么问题?

正常情况下,当使用线程池或异步任务时,原始的ThreadLocal在线程切换或任务执行时无法将设置的值正确传递给子线程或后续任务。这使得在使用线程池或异步编程时,ThreadLocal的使用变得非常复杂。TransmittableThreadLocal通过在线程切换时显式地传递ThreadLocal的值,解决了上述问题。它提供了一种机制,可以自动将ThreadLocal的值从父线程传递到子线程,并确保在整个任务调用链中正确传递。

使用TransmittableThreadLocal时,您可以像使用普通的ThreadLocal一样设置和获取值,但它会自动处理值的传递。这样,即使在线程池或异步任务中,也能够正确地共享和传递ThreadLocal的值。

个人理解:其实就是原有线程池的一个加强版,解决上一个线程带给下一线程一些值传递问题。

TransmitterableThreadLocal的使用

引入jar包

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.14.2</version>
</dependency>

验证demo代码

demo1

package com.ttl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.alibaba.ttl.TransmittableThreadLocal;

public class MyRunnable implements Runnable {
    private static TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();

    @Override
    public void run() {
        threadLocal.set((int) (Math.random() * 100)); // 设置当前线程的值
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("TransmittableThreadLocal value: " + threadLocal.get()); // 获取当前线程的值
    }
}
package com.ttl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: csh
 * @Date: 2023/7/8 20:37
 * @Description:
 * ttl案例,通过 线程池,将上一个线程带给下一线程。
 *
 */
public class StudyTtl {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        MyRunnable sharedRunnable = new MyRunnable();

        executorService.execute(sharedRunnable);
        executorService.execute(sharedRunnable);

        executorService.shutdown();
    }
}

结果

TransmittableThreadLocal value: 99
TransmittableThreadLocal value: 25

demo2

package com.ttl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.alibaba.ttl.TransmittableThreadLocal;

public class TransmittableThreadLocalDemo {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 创建TransmittableThreadLocal实例
        TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();

        // 在主线程设置值
        threadLocal.set(42);
        System.out.println("主线程 - ThreadLocal value: " + threadLocal.get());

        // 提交任务到线程池
        executorService.execute(() -> {
            // 打印子线程中的ThreadLocal值
            System.out.println("子线程 - ThreadLocal value: " + threadLocal.get());
        });

        // 关闭线程池
        executorService.shutdown();
    }
}

结果

主线程 - ThreadLocal value: 42
子线程 - ThreadLocal value: 42

TransmitterableThreadLocal的实现原理

TransmittableThreadLocal的实现原理主要依赖于Java的InheritableThreadLocal和ThreadLocal。

在Java中,InheritableThreadLocal是一个可以在父线程和子线程之间传递值的类。它通过子线程继承父线程的值,并且可以在子线程中对该值进行修改。然而,InheritableThreadLocal并不能满足在线程池或异步任务场景下的需求,因为它仅在线程创建时继承值,而在线程切换 or 任务执行后无法正确地传递值。

TransmittableThreadLocal通过扩展InheritableThreadLocal类,并重写其相关方法,实现了对ThreadLocal值传递的增强。

具体来说,TransmittableThreadLocal的实现原理如下:

在TransmittableThreadLocal中,每个ThreadLocal对象都有一个对应的Holder对象(比如TransmitterableThreadLocal.TransmitterableThreadLocalHolder),用于保存值的传递。

当主线程设置ThreadLocal值时,TransmittableThreadLocal会将值存储在Holder对象中。

当创建子线程时,TransmittableThreadLocal会使用InheritableThreadLocal的特性,将父线程中的Holder对象复制到子线程中。

在子线程中,当通过TransmittableThreadLocal获取值时,它会先检查当前线程是否有Holder对象。如果没有,则会从父线程中获取Holder对象,并拷贝一份到子线程中,以确保值的正确传递。

当父线程的ThreadLocal值发生改变时,TransmittableThreadLocal还会同步更新子线程的对应值,以保持值的一致性。

总结起来,TransmittableThreadLocal的实现机制主要通过继承InheritableThreadLocal类,重写相关方法,并使用Holder对象进行值的传递和同步更新。这样就能够在线程切换或异步任务中正确地传递ThreadLocal的值。

TransmitterableThreadLocal核心源码学习

类图

869f087c32eb93bcd3c836e9eb833d3a.png

com.alibaba.ttl.TransmittableThreadLocal实现如下

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {
    private transient ConcurrentMap<Thread, T> threadLocalValues = new ConcurrentHashMap<>();
    private transient ConcurrentMap<Thread, ThreadLocalMap> inheritableThreadLocals = new ConcurrentHashMap<>();

    @Override
    public void set(T value) {
        Thread currentThread = Thread.currentThread();

        // 检查当前线程是否存在 ThreadLocalMap 对象,如果不存在,则创建
        ThreadLocalMap threadLocals = inheritableThreadLocals.get(currentThread);
        if (threadLocals == null) {
            threadLocals = new ThreadLocalMap();
            inheritableThreadLocals.put(currentThread, threadLocals);
        }

        // 设置当前线程的值
        threadLocals.set(this, value);

        // 存储当前线程的值
        threadLocalValues.put(currentThread, value);
    }
    //初始化值方法
    @Override
    protected T initialValue() {
        return null;
    }
    //获取父类的值
    @Override
    protected T childValue(T parentValue) {
        return parentValue;
    }
    //获取方法
    @Override
    public T get() {
        Thread currentThread = Thread.currentThread();

        // 首先尝试从 threadLocalValues 获取当前线程的值
        T value = threadLocalValues.get(currentThread);
        if (value == null && !threadLocalValues.containsKey(currentThread)) {

            // 如果 threadLocalValues 中不存在当前线程的值,则尝试从 inheritableThreadLocals 获取
            ThreadLocalMap threadLocals = inheritableThreadLocals.get(currentThread);
            if (threadLocals != null) {
                value = threadLocals.get(this);
            }

            // 如果还是没有获取到值,则调用 initialValue() 方法初始化
            if (value == null) {
                value = initialValue();

                // 将值存储到 threadLocalValues 和 inheritableThreadLocals 中
                threadLocalValues.put(currentThread, value);
                if (threadLocals == null) {
                    threadLocals = new ThreadLocalMap();
                    inheritableThreadLocals.put(currentThread, threadLocals);
                }
                threadLocals.set(this, value);
            }
        }
        return value;
    }
    //删除当前线程的内容
    @Override
    public void remove() {
        Thread currentThread = Thread.currentThread();
        threadLocalValues.remove(currentThread);

        ThreadLocalMap threadLocals = inheritableThreadLocals.get(currentThread);
        if (threadLocals != null) {
            threadLocals.remove(this);
        }
    }
}

其它源码可自行了解~

最后

    ttl这个框一般是用于链路跟踪技术的场景,可以将当前线程生成的一个taskid传递到下一个线程,然后由下一个线程来处者或者一些线上特殊的业务场景需要前后或多个线程依次传递的一个场景,但是建议还是通过新增字段或统一缓存比如redis这种来做这种操作,因为如果仅仅是一个线程内的,很有可能因为机器需要重启或一些未考虑到的操作,导致该操作的值丢失,而且这种耦合到你的业务代码中不太合理,特别是关于业务代码这种,如果实在没有必要或有更好的方案建议还是要考虑场景是否合适,本人就因为看过太多同学为了炫耀技术,导致项目代码维护及发现一些很难排查的线上事故~~~

参考文献:

https://segmentfault.com/a/1190000041954190?utm_source=sf-similar-article

https://github.com/alibaba/transmittable-thread-local

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

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

相关文章

MAC电脑查看SHA256方式

背景 现在很多网站下载大文件时&#xff0c;以往通过查看文件大小来确定是否下载正确&#xff0c;但是很多情况下&#xff0c;文件下载后大小差不多&#xff0c;但是很多时候却时候出现无法安装的问题&#xff0c;有可能还是下载的文件出现错误&#xff0c;导致文件无法正常使…

机器学习 day25(softmax在神经网络模型上的应用)

输出层采用softmax 在识别手写数字的模型中&#xff0c;预测y只有两个结果&#xff0c;所以输出层采用sigmoid激活函数且只有一个神经元。若预测y有10个结果&#xff08;0-9&#xff09;&#xff0c;该模型的前向传播计算方式与识别数字的模型完全相同&#xff0c;即隐藏层的…

dubbo概念及基本架构

一. Dubbo概念 1、Dubbo是一个开源的高性能&#xff0c;轻量级的Java RPC框架 RPC&#xff1a;远程服务调用 2、RPC的远程服务调用和SOA的服务治理两种功能。 二. Dubbo架构 0、服务提供方首先启动 1、服务提供方启动后&#xff0c;将其注射到注册中心里 2、消费者想调用提供…

线程随机性

目录 线程随机性的展现 执行start()的顺序不代表执行run()的顺序 在使用多线程技术时&#xff0c;代码运行的结果与代码执行顺序或者调用顺序无关。线程是一个子任务&#xff0c;而CPU是以不确定的方式&#xff0c;或者说是以随机的时间来调用线程中的run方法的。所以说线程执…

nvm 下载nodejs 失败

解决办法&#xff1a; 1.查看nvm安装路径 nvm root2、在安装路径下找到setting.txt,添加两句话 node_mirror: http://npm.taobao.org/mirrors/node/ npm_mirror: https://npm.taobao.org/mirrors/npm/3.再执行nvm install 就可以了。

【uniapp开发小程序】实现读取手机剪切板第一条,识别并以姓名/手机号/收货地址格式输出

效果图&#xff1a; 完整代码&#xff1a; <template><view class""><text>测试</text><view click"pasteContent()" class"content">点击此处可快速识别 您的收货信息</view></view> </templat…

使用chatGPT + AI 绘图生成自己的专属头像

案例介绍 微信头像是朋友认识我们时的第一印象,或许是可爱、妖娆,或许是帅气、成熟,还有自然、厚重、调皮… 我们都有自己独特的故事,独特的思想,独特的爱好,对于头像当然有着自己独到的设计眼光。 接下来请允许我向大家展示如何使用chatGPT、AI绘图工具生成出自己的专属…

【vue学习】权限管理系统前端实现6-主页面布局

1.新建layout文件夹 新建index.vue 添加router const routes [{path: /,name: 首页,component: () > import(../layout)}, 2.登录添加跳转 loginRef.value.validate(async (valid)>{if(valid){try{let resultawait requestUtil.post("login?"qs.stringify(l…

人工智能面试总结-Transformer专题

B站:啥都会一点的研究生 公众号试读:啥都会一点的研究生 目录 说说什么是Transformer?说说Transformer中的Encoder?说说Transformer中的Decoder?说说Transformer在训练与测试阶段Decoder的输入、输出是什么?说说Transformer Encoder和Decoder有哪些不同?说说什么是Tran…

Qt/C++原创项目作品精选(祖传原创/性能凶残)

00 前言说明 从事Qt开发十年有余&#xff0c;一开始是做C#.NET开发的&#xff0c;因为项目需要&#xff0c;转行做嵌入式linux开发&#xff0c;在嵌入式linux上做可视化界面开发一般首选Qt&#xff0c;当然现在可选的方案很多比如安卓&#xff0c;但是十多年前那时候板子性能低…

松下|PCB焊盘脱落常见的几个原因分析

PCB焊盘脱落是电子制造中常见的问题之一&#xff0c;它会导致电路板的性能下降或完全失效。本文将从几个常见原因来分析PCB焊盘脱落的原因&#xff0c;并提供一些预防措施。 一、设计问题 PCB焊盘脱落的一个常见原因是设计问题。一些设计错误可能会导致焊盘的结构不稳定&…

QCC51XX---SPI使用

QCC51XX---系统学习目录_嵌入式学习_force的博客-CSDN博客 了解了I2C的使用,细心的小伙伴已经发现了,在bitserial这个功能里面还能使用SPI。bitserial就是用来使用I2C和SPI的,而这两者的使用,大同小异。 在默认代码使中,是有两个传感器的驱动代码的,分别是加速度传感器ad…

若依@DataScope

DataScope &#xff1a; 根据sys_role 中的 data_scope中的值&#xff0c;来设置数据该怎么过滤&#xff0c;&#xff0c; data_scope 取值&#xff1a; 1 &#xff1a; 不需要过滤2 &#xff1a; 自定义过滤 &#xff1a; 根据 sys_role_dept 这个表关联出来的 dept_id 过滤…

五.LLC谐振变换器

LLC 谐振变换器启动过程分析 LLC 谐振变换器的组成结构中包含容性器件&#xff0c;为了尽可能减小输出电压纹波&#xff0c;钳位输出电压&#xff0c;此时希望输出滤波电容尽可能的大&#xff0c;因此也会在启动的时候&#xff0c;电容两端电压近似为 0&#xff0c;系统对电容…

Final2x-开源图片无损放大工具 提升分辨率到任意尺寸

一、Final2x是什么 Final2x是一款开源、跨平台的图片无损放大工具&#xff0c;内置多个模型&#xff0c;能够将图片提升分辨率到任意尺寸&#xff0c;增强图像的分辨率和质量&#xff0c;使图像更清晰、更详细。目前支持RealCUGAN、RealESRGAN、Waifu2x、SRMD等多种模型。支持…

不会写代码,咋做爬虫

随着时代的发展&#xff0c;大数据越来越重要&#xff0c;数据获取很关键 提到数据获取大家应该都会想到爬虫&#xff0c;但是我不会写代码怎么玩爬虫 今天给大家分享一个不会代码也可以进行爬虫的工具&#xff0c;实现无代码进行数据获取 强调 这里介绍的工具大家合理合法使用…

使用黑盒测试在 Go 中重写 Bash 脚本

目录 前言&#xff1a; 开始 准备工作 描述行为&#xff1a;Bats 简介 行为描述&#xff1a;陷阱 描述行为&#xff1a;设计测试 重写&#xff1a;让我们开始用go吧&#xff01; 重构和更新&#xff1a;实现胜利 结论 前言&#xff1a; 使用黑盒测试在Go中重写Bash脚本…

Linux bio数据结构

数据结构 /** main unit of I/O for the block layer and lower layers (ie drivers and* stacking drivers)*/ struct bio {struct bio *bi_next; /* request queue link */struct gendisk *bi_disk;unsigned int bi_opf; /* bottom bits req flags,* top bits REQ_OP. Us…

go入门demo

go快速学习&#xff1a; 官网学习方案&#xff08;选择自己喜欢的语言&#xff09;&#xff1a;A Tour of Go 视频:B站是个不错的选择 &#xff08;转go入门使用&#xff09;推荐视频&#xff1a;1-课程需知与课程提纲_哔哩哔哩_bilibili1-课程需知与课程提纲是8小时转职Gol…

把握住golang中的template,方能驾驭得了Hugo主题的template

笔者小站&#xff1a;秋码记录 不置可否&#xff0c;Hugo的template同样是使用golang的标准库html/template。为了能实现一个属于自己独特的Hugo theme&#xff0c;或是修改他人的主题&#xff0c;都得对其模板语法有所知晓&#xff0c;方能改的称心如意&#xff0c;亦或是制作…