当前位置: 首页 > news >正文

阿里巴巴TransmittableThreadLocal使用指南

前言

ThreadLocal在上下文的数据传输上非常的方便和简洁。工业实践中,比较常用的有三个,ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal,那么他们三个之间有什么区别呢?

常见的三种ThreadLocal比较

ThreadLocalInheritableThreadLocalTransmittableThreadLocal
来源jdkjdk阿里开源
单线程数据传输支持支持支持
new线程数据传输不支持支持支持
线程池数据传输不支持部分支持【简单场景】支持

针对线程池的数据传输,InheritableThreadLocal仅仅能在一些简单场景下做到,下面就用一个案例来说明

先看下线程工厂的定义

package com.tml.mouseDemo.core.threadLocalDemo;import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;public class SimpleThreadFactory implements ThreadFactory {private static AtomicInteger atomicInteger = new AtomicInteger(1);@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setName("tml-"+atomicInteger.getAndIncrement());return t;}
}

 InheritableThreadLocal部分支持的案例

package com.tml.mouseDemo.core.threadLocalDemo;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadLocalDemo1 {private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());public static void main(String[] args) throws InterruptedException {threadLocal.set("hello main");for (int i = 0; i < 2; i++) {service.execute(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);});}//修改threadLocal中的值threadLocal.set("hello world");Thread.sleep(2000);for (int i = 0; i < 2; i++) {service.execute(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);});}ThreadUtils.printLog("get data " + threadLocal.get());service.shutdown();}
}

运行结果如下 

2025-01-10 19:41:50 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:50 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:52 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:52 | INFO  | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:41:52 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main

从运行结果来看,前面的两次循环提交的任务,在子线程中确实是能正常的获取主线程设置的变量,即hello main,但是紧接着,我修改了主线程上绑定的变量为hello world,然后继续循环两次提交两个任务,这个时候子线程中获取的线程变量依然是hello main,这明显是与是期望不一致的。

从这个层面来讲,InheritableThreadLocal确实在线程池的层面支持不够友好,可以说仅支持部分简单场景。

根本原因就死线程池的池化机制,从上面的运行日志上也可以看出,提交了4个任务执行的线程依然是两个。线程池中的线程是复用的,InheritableThreadLocal是在创建子线程的时候,会将主线程上绑定的数据拷贝过来,如果我不创建新的线程,但是主线程上绑定的数据改变了呢?那我依然还是读取到之前拷贝的数据。

这个就是InheritableThreadLocal的短板。针对这个问题,阿里开源的TransmittableThreadLocal就能顺利丝滑的解决这个问题。

TransmittableThreadLocal实践

maven依赖

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

装饰Runnable任务

直接看代码

package com.tml.mouseDemo.core.threadLocalDemo;import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadLocalDemo1 {private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());public static void main(String[] args) throws InterruptedException {threadLocal.set("hello main");for (int i = 0; i < 2; i++) {service.execute(TtlRunnable.get(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);}));}//修改threadLocal中的值threadLocal.set("hello world");Thread.sleep(2000);for (int i = 0; i < 2; i++) {service.execute(TtlRunnable.get(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);}));}ThreadUtils.printLog("get data " + threadLocal.get());service.shutdown();}
}

运行结果如下:

 2025-01-10 19:57:03 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:57:03 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:57:05 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:57:05 | INFO  | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:57:05 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world

与第一个案例的差异点在于,使用了

public static TtlRunnable get(@Nullable Runnable runnable) {return get(runnable, false, false);
}

 来增强了了Runnable任务,执行的结果也是符合预期。

装饰线程池

直接看代码

package com.tml.mouseDemo.core.threadLocalDemo;import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;
import com.alibaba.ttl.threadpool.TtlExecutors;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadLocalDemo1 {private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());private static ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(service);public static void main(String[] args) throws InterruptedException {threadLocal.set("hello main");for (int i = 0; i < 2; i++) {ttlExecutorService.execute(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);});}//修改threadLocal中的值threadLocal.set("hello world");Thread.sleep(2000);for (int i = 0; i < 2; i++) {ttlExecutorService.execute(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);});}ThreadUtils.printLog("get data " + threadLocal.get());service.shutdown();}
}

 运行结果如下

2025-01-10 20:05:05 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:05:05 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:05:07 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:05:07 | INFO  | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:05:07 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world

与上一个案例的差异点在于,这里没有包装Runnable任务,而是包装了线程池,使用了

public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {if (TtlAgent.isTtlAgentLoaded() || executorService == null || executorService instanceof TtlEnhanced) {return executorService;}return new ExecutorServiceTtlWrapper(executorService, true);
}

包装了ExecutorService,执行结果也是符合预期

使用java Agent无侵入增强线程池

直接看代码

package com.tml.mouseDemo.core.threadLocalDemo;import com.alibaba.ttl.TransmittableThreadLocal;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadLocalDemo1 {private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());public static void main(String[] args) throws InterruptedException {threadLocal.set("hello main");for (int i = 0; i < 2; i++) {service.execute(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);});}//修改threadLocal中的值threadLocal.set("hello world");Thread.sleep(2000);for (int i = 0; i < 2; i++) {service.execute(() -> {String s = threadLocal.get();ThreadUtils.printLog("get data " + s);});}ThreadUtils.printLog("get data " + threadLocal.get());service.shutdown();}
}

 项目运行的时候,需要添加额外的jvm启动参数,如下

运行结果如下,也是符合预期

 2025-01-10 20:11:59 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:11:59 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:12:01 | INFO  | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:12:01 | INFO  | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:12:01 | INFO  | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world

总结

阿里巴巴的TransmittableThreadLocal是继承自InheritableThreadLocal,对他的功能进行了增强,增强的点也主要是在线程池的支持上。

通过上面的三个案例,可以看到TransmittableThreadLocal是非常灵活的,大家可以根据自己的需要,选择对应的方式来实现。

TransmittableThreadLocal的官网GitCode - 全球开发者的开源社区,开源代码托管平台

相关文章:

阿里巴巴TransmittableThreadLocal使用指南

前言 ThreadLocal在上下文的数据传输上非常的方便和简洁。工业实践中&#xff0c;比较常用的有三个&#xff0c;ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal&#xff0c;那么他们三个之间有什么区别呢&#xff1f; 常见的三种ThreadLocal比较 ThreadLoc…...

ubuntu20下编译linux1.0 (part1)

author: hjjdebug date: 2025年 01月 09日 星期四 15:56:15 CST description: ubuntu20下编译linux1.0 (part1) 该博客记录了新gcc编译旧代码可能碰到的问题和解决办法, 可留作参考 操作环境: ubuntu20 $ gcc --version gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0 $ as --vers…...

欧拉公式和傅里叶变换

注&#xff1a;英文引文机翻&#xff0c;未校。 中文引文未整理去重&#xff0c;如有异常&#xff0c;请看原文。 Euler’s Formula and Fourier Transform Posted byczxttkl October 7, 2018 Euler’s formula states that e i x cos ⁡ x i sin ⁡ x e^{ix} \cos{x} i …...

Jenkins内修改allure报告名称

背景&#xff1a; 最近使用Jenkins搭建自动化测试环境时&#xff0c;使用Jenkins的allure插件生成的报告&#xff0c;一直显示默认ALLURE REPORT&#xff0c;想自定义成与项目关联的名称&#xff0c;如图所示&#xff0c;很明显自定义名称显得高大上些&#xff0c;之前…...

30天开发操作系统 第 12 天 -- 定时器 v1.0

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单&#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器&#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…...

Ubuntu | PostgreSQL | 解决 ERROR: `xmllint` is missing on your system.

解决 sudo apt install apt-file sudo apt-file updatesudo apt-file search xmllint sudo apt install libxml2-utils执行 # postgres源码安装包解压文件夹中 make install make install问题 make -C src install make[2]: Entering directory /home/postgres/postgresql-1…...

uniapp使用chooseLocation安卓篇

本文章全部以高德地图为例 代码 <view class"bottom"><button click"choose">定位</button> </view> choose() {uni.chooseLocation({success: function(res) {console.log(位置名称&#xff1a; res.name);console.log(详细地…...

《PC 上的开源神经网络多模态模型:开启智能交互新时代》

《PC 上的开源神经网络多模态模型&#xff1a;开启智能交互新时代》 一、引言二、多模态模型基础剖析&#xff08;一&#xff09;核心概念解读&#xff08;二&#xff09;技术架构探秘 三、开源多模态模型的独特魅力&#xff08;一&#xff09;开源优势尽显&#xff08;二&…...

Apache JMeter 压力测试使用说明

文章目录 一、 安装步骤步骤一 下载相关的包步骤二 安装 Jmeter步骤三 设置 Jmeter 工具语言类型为中文 二、使用工具2.1 创建测试任务步骤一 创建线程组步骤二 创建 HTTP 请求 2.2 配置 HTTP 默认参数添加 HTTP消息头管理器HTTP请求默认值 2.3 添加 查看结果监听器2.4 查看结果…...

腾讯云AI代码助手编程挑战赛-知识百科AI

作品简介 知识百科AI这一编程主要用于对于小朋友的探索力的开发&#xff0c;让小朋友在一开始就对学习具有探索精神。在信息化时代下&#xff0c;会主动去学习自己认知以外的知识&#xff0c;同时丰富了眼界&#xff0c;开拓了新的知识。同时催生了在大数据时代下的信息共享化…...

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

前言 &#x1f31f;&#x1f31f;本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &am…...

HTTP-响应协议

HTTP的响应过程&#xff1f; 浏览器请求数据--》web服务器过程&#xff1a;请求过程 web服务器将响应数据-》到浏览器&#xff1a;响应过程 响应数据有哪些内容&#xff1f; 1.和请求数据类似。 2. 响应体中存储着web服务器返回给浏览器的响应数据。并且注意响应头和响应体之间…...

SQL进阶实战技巧:即时订单比例问题

目录 0 需求描述 1 数据准备 2 问题分析 3 小结 往期精彩 0 需求描述 订单配送中,如果期望配送日期和下单日期相同,称为即时订单,如果期望配送日期和下单日期不同,称为计划订单。 请从配送信息表(delivery_info)中求出每个用户的首单(用户的第一个订单)中即时订单…...

什么是端口

端口是用来区分同一网络设备(IP地址)上运行的不同服务或应用程序接收外部数据的窗口。 以下是几个要点&#xff1a; 对于我们发送请求指定的url中的端口&#xff0c;指的是对方服务器的用于接收数据的端口&#xff0c;如http的80端口&#xff0c;服务器通常都会设定要监听来自…...

【Flutter】使用ScrollController配合EasyRefresh实现列表预加载:在还未滑动到底部时加载下一页数据

需求/背景 在我们的业务场景中&#xff0c;列表的加载使用easy_refresh组件&#xff1a; https://pub.dev/packages/easy_refresh 大概效果是往上滑动到一定的offset会触发一个上滑加载&#xff0c;可以触发一些网络请求拉取列表后面的数据来展示。 这种模式一般在一页翻完…...

【2025 Rust学习 --- 11 实用工具特型01】

清理特型Drop 当一个值的拥有者消失时&#xff0c;Rust 会丢弃&#xff08;drop&#xff09;该值。丢弃一个值就必须释放 该值拥有的任何其他值、堆存储和系统资源。 丢弃可能发生在多种情况下&#xff1a; 当变量超出作用域时&#xff1b;在表达式语句的末尾&#xff1b;当…...

网络安全基础以及概念

1. 安全领域的概念 1.1 网络产品 1. EDR:终端检测与响应(Endpoint Detection and Response),终端主要包括我们的笔记本、台式机、手机、服务器等,EDR是一种运行在终端上安全软件,主要负责监控网络流量、可疑进程、注册表活动等其他安全相关的事件与活动。当发现有威胁是自…...

windows和linux的抓包方式

1.实验准备&#xff1a; 一台windows主机&#xff0c;一台linux主机 wireshark使用&#xff1a; 打开wireshark&#xff0c;这些有波动的就代表可以有流量经过该网卡&#xff0c;选择一张有流量经过的网卡 可以看到很多的流量&#xff0c;然后可以使用过滤器来过滤想要的流量…...

【Uniapp-Vue3】v-if条件渲染及v-show的选择对比

如果我们想让元素根据响应式变量的值进行显示或隐藏可以使用v-if或v-show 一、v-show 另一种控制显示的方法就是使用v-show&#xff0c;使用方法和v-if一样&#xff0c;为true显示&#xff0c;为false则不显示。 二、v-if v-if除了可以像v-show一样单独使用外&#xff0c;还…...

宝塔面板使用 GoAccess Web 日志分析教程

宝塔面板是一个简单方便的服务器运维面板,但其网站统计功能是收费的。而 GoAccess 是一个用 C 编写的免费开源 Web日志分析器,本文将介绍如何在宝塔面板中开启 GoAccess Web 日志分析功能。 内容索引 下载安装 GoAccess在宝塔面板中添加日志切割的计划任务将 Web 日志输出到…...

手把手教你定制专属标注工具:基于Python3+Tkinter打造你的实体关系标注器

从零构建领域专用标注工具&#xff1a;Python3Tkinter实战指南 在自然语言处理项目中&#xff0c;高质量标注数据是模型效果的基石。当面对法律条文、医疗报告等专业领域时&#xff0c;通用标注工具往往难以满足特定实体关系和输出格式需求。本文将带你深入开发一个完全可控的实…...

从沙子到车辙(3.3):数据通路与控制器的“双人舞“

3.3 数据通路与控制器的"双人舞" &#x1f4da; 本文内容摘自本人的开源书《从沙子到车辙 - 一个工程师的理解》 &#x1f517; 在线阅读/下载&#xff1a;from-sand-to-ruts git clone https://github.com/Lularible/from-sand-to-ruts⭐ 如果对您有帮助&#xf…...

Rocky Linux 9.0上5分钟搞定NFS共享:从安装到挂载的保姆级避坑指南

Rocky Linux 9.0极速部署NFS共享&#xff1a;零基础到精通的实战手册 当你在凌晨两点接到紧急任务&#xff0c;需要在Rocky Linux 9.0上为开发团队搭建临时文件共享环境时&#xff0c;传统教程里冗长的配置步骤和晦涩的错误排查足以让人崩溃。本文专为解决这类"救火场景&q…...

全志T3工业级评估板深度评测:国产化、接口性能与Docker容器化实践

1. 开箱初探&#xff1a;一份诚意满满的工业级“全家桶”作为一名在嵌入式硬件开发领域摸爬滚打了十多年的老工程师&#xff0c;我经手过的评估板、开发板少说也有上百款。从早期的ARM9到现在的多核A系列、RISC-V&#xff0c;每次开箱都像是一次探险。但这次拿到创龙科技&#…...

从门电路到芯片:拆解一个D触发器,看数字电路如何实现‘记忆’这个核心功能

从门电路到芯片&#xff1a;拆解一个D触发器&#xff0c;看数字电路如何实现‘记忆’这个核心功能 数字世界的每一个比特信息都需要被精确存储和传递&#xff0c;而实现这一功能的核心元件便是触发器。当我们按下电脑的电源键&#xff0c;屏幕上闪现的第一个像素到硬盘中保存的…...

VAP特效动画创作指南:3步打造跨平台炫酷视觉特效

VAP特效动画创作指南&#xff1a;3步打造跨平台炫酷视觉特效 【免费下载链接】vap VAP是企鹅电竞开发&#xff0c;用于播放特效动画的实现方案。具有高压缩率、硬件解码等优点。同时支持 iOS,Android,Web 平台。 项目地址: https://gitcode.com/gh_mirrors/va/vap 还在为…...

在Taotoken控制台清晰观测各模型用量与成本消耗情况

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在Taotoken控制台清晰观测各模型用量与成本消耗情况 接入多个大语言模型进行开发时&#xff0c;一个常见的困扰是成本不透明。调用…...

告别集群负载:用Docker Compose在外部机器部署Prometheus+Grafana监控K8S(附完整配置文件)

轻量化监控方案&#xff1a;Docker Compose 部署 PrometheusGrafana 监控 Kubernetes 集群 对于资源有限的中小团队或个人开发者来说&#xff0c;将监控系统与业务集群分离是一个明智的选择。传统的 Kubernetes 监控方案通常将 Prometheus 和 Grafana 部署在集群内部&#xff0…...

别急着重装!用注册表和任务管理器两步修复Edge浏览器无法上网(Win10/Win11通用)

深度修复Edge浏览器网络故障&#xff1a;注册表与进程管理的终极指南 当Edge浏览器突然无法连接网络时&#xff0c;大多数用户的第一反应往往是重装浏览器甚至操作系统。但作为一名长期与Windows系统打交道的技术顾问&#xff0c;我发现90%的类似问题其实无需如此大动干戈。本文…...

告别键盘鼠标切换烦恼:开源KVM软件Input Leap让你一套键鼠控制多台电脑

告别键盘鼠标切换烦恼&#xff1a;开源KVM软件Input Leap让你一套键鼠控制多台电脑 【免费下载链接】input-leap Open-source KVM software 项目地址: https://gitcode.com/gh_mirrors/in/input-leap 你是否经常在Windows、macOS和Linux多台电脑之间来回切换&#xff0c…...