当前位置: 首页 > 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 日志输出到…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...