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

【多线程开发 2】从代码到实战TransmittableThreadLocal

【多线程开发 2】从代码到实战TransmittableThreadLocal

本文将从以下几个点讲解TransmittableThreadLocal(为了方便写以下简称ttl):

  • 前身

  • 是什么?

  • 可以用来做什么?

  • 源码原理

  • 实战

前身

ThreadLocal

要了解ttl就要先了解Java自带的类ThreadLocal,threadlocal是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,,用于存储一些线程不安全的公共变量,通过“给每一个线程一个线程不安全的变量的拷贝”,来达到线程安全的目的,就不会出现变量多个线程之间共享的问题。

ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

InheritableThreadLocal

使用ThreadLocal可以解决线程安全问题,但是也有一定的局限性,比如无法在父子线程之间传递信息,因此InheritableThreadLocal就是JDK为了解决这个问题而创建的

TTL是什么?可以做什么?

在现在开发的情况下肯定是需要复用线程的,如果说InheritableThreadLocal在生成子进程的时候会做信息传递,但是在使用线程池或者其他需要复用线程的地方,由于会不产生新的Thread,而是直接使用空闲的已建成的Thread,所以的话InheritableThreadLocal有时候会不能用,此时可以通过使用TTL解决对应问题。

img

读取线程间传递的ThreadLocal 值比较麻烦,ThreadLocal 和 InheritableThreadLocal 都没有开放内部的 ThreadLocalMap,不能直接读取。所以TTL继承了InheritableThreadLocal,在每次调用 ThreadLocal的 set/get/remove 等接口的时候,为 Thread 记录到底绑定了哪些需要发生线程间传递的 ThreadLocal 对象。

在创建runnable的时候,TTL会通过holder遍历全部的TTLRunnable快照,看出上下文中有哪些线程上的信息需要进行复制。

TTL的GitHub项目地址:https://github.com/alibaba/transmittable-thread-local,感兴趣的话可以查看一下源码,后续有时间的话我会出一篇详细解析TTL源码的项目地址。

实战

其中需要使用拦截器辅助实现,需要读者依据相应技术架构自行实现

使用TTL实现MDC日志在多系统之间的信息传输

public class CustomMdcAdapters implements MDCAdapter {private static final int WRITE_OPERATION = 1;private static final int MAP_COPY_OPERATION = 2;private static CustomMdcAdapters mtcMDCAdapter;static {mtcMDCAdapter = new CustomMdcAdapters();MDC.mdcAdapter = mtcMDCAdapter;}private final ThreadLocal<Map<String, String>> copyOnInheritThreadLocal = new TransmittableThreadLocal<>();private final ThreadLocal<Integer> lastOperation = new ThreadLocal<>();public static MDCAdapter getInstance() {return mtcMDCAdapter;}private static boolean wasLastOpReadOrNull(Integer lastOp) {return lastOp == null || lastOp == MAP_COPY_OPERATION;}private Integer getAndSetLastOperation(int op) {Integer lastOp = lastOperation.get();lastOperation.set(op);return lastOp;}private Map<String, String> duplicateAndInsertNewMap(Map<String, String> oldMap) {Map<String, String> newMap = Collections.synchronizedMap(new HashMap<>(16));if (oldMap != null) {// we don't want the parent thread modifying oldMap while we are// iterating over itsynchronized (oldMap) {newMap.putAll(oldMap);}}copyOnInheritThreadLocal.set(newMap);return newMap;}@Overridepublic void put(String key, String val) {if (key == null) {throw new IllegalArgumentException("key cannot be null");}Map<String, String> oldMap = copyOnInheritThreadLocal.get();Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);if (wasLastOpReadOrNull(lastOp) || oldMap == null) {Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);newMap.put(key, val);} else {oldMap.put(key, val);}}@Overridepublic void remove(String key) {if (key == null) {return;}Map<String, String> oldMap = copyOnInheritThreadLocal.get();if (oldMap == null) {return;}Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);if (wasLastOpReadOrNull(lastOp)) {Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);newMap.remove(key);} else {oldMap.remove(key);}}@Overridepublic void clear() {lastOperation.set(WRITE_OPERATION);copyOnInheritThreadLocal.remove();}@Overridepublic String get(String key) {final Map<String, String> map = copyOnInheritThreadLocal.get();if ((map != null) && (key != null)) {return map.get(key);} else {return null;}}public Map<String, String> getPropertyMap() {lastOperation.set(MAP_COPY_OPERATION);return copyOnInheritThreadLocal.get();}public Set<String> getKeys() {Map<String, String> map = getPropertyMap();if (map != null) {return map.keySet();} else {return null;}}@Overridepublic Map<String, String> getCopyOfContextMap() {Map<String, String> hashMap = copyOnInheritThreadLocal.get();if (hashMap == null) {return null;} else {return new HashMap<>(hashMap);}}@Overridepublic void setContextMap(Map<String, String> contextMap) {lastOperation.set(WRITE_OPERATION);Map<String, String> newMap = Collections.synchronizedMap(new HashMap<>(16));newMap.putAll(contextMap);copyOnInheritThreadLocal.set(newMap);}
}

使用TTL帮助实现web服务中的用户信息传输

public final class ContextsUtils {private ContextsUtils() {}private static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new TransmittableThreadLocal<>();public static void putAll(Map<String, String> map) {map.forEach(ContextsUtils::set);}public static void set(String key, Object value) {Map<String, String> map = getLocalMap();map.put(key, value == null ? StrPool.EMPTY : value.toString());}public static <T> T get(String key, Class<T> type) {Map<String, String> map = getLocalMap();return Convert.convert(type, map.get(key));}public static <T> T get(String key, Class<T> type, Object def) {Map<String, String> map = getLocalMap();return Convert.convert(type, map.getOrDefault(key, String.valueOf(def == null ? StrPool.EMPTY : def)));}public static Map<String, String> getLocalMap() {Map<String, String> map = THREAD_LOCAL.get();if (map == null) {map = new ConcurrentHashMap<>(10);THREAD_LOCAL.set(map);}return map;}/*** 其他get/set各种信息需要读者依据业务自行实现。。。*/}

相关文章:

【多线程开发 2】从代码到实战TransmittableThreadLocal

【多线程开发 2】从代码到实战TransmittableThreadLocal 本文将从以下几个点讲解TransmittableThreadLocal(为了方便写以下简称ttl)&#xff1a; 前身 是什么&#xff1f; 可以用来做什么&#xff1f; 源码原理 实战 前身 ThreadLocal 要了解ttl就要先了解Java自带的类…...

【车载以太网测试从入门到精通】——SOME/IP协议测试

系列文章目录 【车载以太网测试从入门到精通】系列文章目录汇总 文章目录 系列文章目录前言一、SOME/IP时间参数1.INITIAL_DELAY时间2.REPETITIONS_MAX次数3.REPETITIONS_BASE_DELAY时间4.CYCLIC_OFFER_DELAY时间5.TIME_TO_LIVE时间6.SUBSCRIBE_RETRY_DELAY时间二、SOME/IP服务…...

作业39 sqrt应用

目录 判断完全平方数 题目描述 输出所有因数 题目描述 因子求和 题目描述 判断素数 题目描述 判断完全平方数 题目描述 输入一个整数&#xff0c;判断他是否是完全平方数&#xff0c;如果是&#xff0c;输出yes&#xff0c;否则输出no 样例 样例…...

springboot 实现跨域的几种方式

1、跨域的原因&#xff1a; 由于同源策略(Same Origin Policy)的限制,浏览器不允许跨域请求。同源策略规定,A网页设置的Cookie、LocalStorage和IndexDB无法被同源以外的网页读取。 2、原因&#xff1a; 1&#xff09;浏览器的同源策略(Same Origin Policy)限制了跨域请求。主要…...

springmvc Web上下文初始化

Web上下文初始化 web上下文与SerlvetContext的生命周期应该是相同的&#xff0c;springmvc中的web上下文初始化是由ContextLoaderListener来启动的 web上下文初始化流程 在web.xml中配置ContextLoaderListener <listener> <listener-class>org.springframework.…...

Verilog实战学习到RiscV - 2 : wire 和 reg 的区别

Verilog: wire 和 reg 的区别 1 引言 看Verilog例子过程中&#xff0c;总是分不清 wire 和 reg 的区别。这篇文章把两者放在一起总结一下&#xff0c;并且对比何时使用它们。 1.1 wire &#xff1a;组合逻辑 wire 是 Verilog 设计中的简单导线&#xff08;或任意宽度的总线…...

OpenGL给定直线起点和终点不同的颜色,使用中点Bresenham画线

用鼠标左键按下确定直线起点&#xff0c;鼠标左键抬起确定直线终点。放一部分代码。 // 中点Bresenham算法.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h" #include <GL/glut.h> #include <iostream> #include <cmath>int windowWidt…...

IT行业的现状与未来发展趋势:从云计算到量子计算的技术变革

随着技术的不断进步&#xff0c;IT行业已经成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链&#xff0c;这些技术正在重塑我们的生活和工作方式。本文将深入探讨当前IT行业的现状&#xff0c;并展望未来发展趋势&#xff0c;旨在为…...

电脑远程控制另一台电脑怎么弄?

可以远程控制另一台电脑吗&#xff1f; “你好&#xff0c;我对远程访问技术不太了解。现在&#xff0c;我希望我的朋友可以远程控制我的Windows 10电脑&#xff0c;以便她能帮我解决一些问题。请问&#xff0c;有没有免费的方法可以实现这种远程控制&#xff1f;我该如何操作…...

软件设计师备考 | 案例专题之面向对象设计 概念与例题

相关概念 关系 依赖&#xff1a;一个事物的语义依赖于另一个事物的语义的变化而变化 关联&#xff1a;一种结构关系&#xff0c;描述了一组链&#xff0c;链是对象之间的连接。分为组合和聚合&#xff0c;都是部分和整体的关系&#xff0c;其中组合事物之间关系更强。两个类之…...

UniApp 2.0可视化开发工具:引领前端开发新纪元

一、引言 在移动互联网迅猛发展的今天&#xff0c;移动应用开发已经成为前端开发的重要方向之一。为了简化移动应用开发流程&#xff0c;提高开发效率&#xff0c;各大开发平台不断推出新的工具和框架。UniApp作为一款跨平台的移动应用开发框架&#xff0c;自诞生以来就备受开…...

前端调用浏览器录音功能且生成文件(vue)

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; 首先在页面中给两个按钮&#xff0c;分别是“开始录音”&#xff0c;“结束录音”。以及录音成功后生成一个下载语音的链接。 1. 先看页面展示 <template><div><button click"startRecording…...

「大数据」Kappa架构

Kappa架构是一种处理大数据的架构&#xff0c;它作为Lambda架构的替代方案出现。Kappa架构的核心思想是简化数据处理流程&#xff0c;通过使用单一的流处理层来同时处理实时和批量数据&#xff0c;从而避免了Lambda架构中需要维护两套系统&#xff08;批处理层和速度层&#xf…...

详细分析Element Plus中的ElMessageBox弹窗用法(附Demo及模版)

目录 前言1. 基本知识2. Demo3. 实战4. 模版 前言 由于需要在登录时&#xff0c;附上一些用户说明书的弹窗 对于ElMessageBox的基本知识详细了解 可通过官网了解基本的语法知识ElMessageBox官网基本知识 1. 基本知识 Element Plus 是一个基于 Vue 3 的组件库&#xff0c;其中…...

Python自动化工具(桌面自动化、Web自动化、游戏辅助)

工具介绍 连点工具是一款可以模拟键鼠后台操作的连点器工具。支持鼠标连点、键鼠脚本录制&#xff0c;支持辅助您实现办公自动化以及辅助游戏操作。功能简洁易用&#xff0c;非常方便操作。连点工具让您在在玩游戏、网购抢购的时候全自动点击鼠标&#xff01;主要功能有&#…...

opencv进阶 ——(五)图像处理之马赛克

一、遍历图像并对每个马赛克区域进行像素化处理 for (int y 0; y < image.rows; y blockSize) {for (int x 0; x < image.cols; x blockSize) {cv::Rect rect cv::Rect(x, y, std::min(blockSize, image.cols - x), std::min(blockSize, image.rows - y));cv::Scal…...

电机控制系列模块解析(22)—— 零矢量刹车

一、零矢量刹车 基本概念 逆变器通常采用三相桥式结构&#xff0c;包含六个功率开关元件&#xff08;如IGBT或MOSFET&#xff09;&#xff0c;分为上桥臂和下桥臂。每个桥臂由两个反并联的开关元件组成&#xff0c;上桥臂和下桥臂对应于电机三相绕组的正负端。正常工作时&…...

自定义一个SpringBoot场景启动器

前言 一个刚刚看完SpringBoot自动装配原理的萌新依据自己的理解写下的文章&#xff0c;如有大神发现错误&#xff0c;敬请斧正&#xff0c;不胜感激。 分析SpringBoot自动配置原理 SpringBoot的启动从被SpringBootApplication修饰的启动类开始,SpringBootApplicaiotn注解中最…...

UDP的报文结构和注意事项

UDP协议是在传输层的协议。 UDP无连接&#xff0c;不可靠传输&#xff0c;面向数据报&#xff0c;全双工。 UDP的报文结构 学习网络协议&#xff0c;最主要的就是报文格式。 对于UDP来说&#xff0c;应用层的数据到达&#xff0c;UDP之后&#xff0c;就会给应用层的数据报前面…...

rust语言一些规则学习

目录 rust中迭代器的使用&#xff08;iter().map()与for循环的区别&#xff09;map()与for的描述区别总结 最后更新时间2024-05-24 rust中迭代器的使用&#xff08;iter().map()与for循环的区别&#xff09; map()与for的描述 rust源码中关于iter().map()函数的解释&#xff…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

在 Spring Boot 项目里,MYSQL中json类型字段使用

前言&#xff1a; 因为程序特殊需求导致&#xff0c;需要mysql数据库存储json类型数据&#xff0c;因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...