【多线程开发 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 it
            synchronized (oldMap) {
                newMap.putAll(oldMap);
            }
        }

        copyOnInheritThreadLocal.set(newMap);
        return newMap;
    }

    @Override
    public 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);
        }
    }

    @Override
    public 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);
        }

    }

    @Override
    public void clear() {
        lastOperation.set(WRITE_OPERATION);
        copyOnInheritThreadLocal.remove();
    }

    @Override
    public 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;
        }
    }

    @Override
    public Map<String, String> getCopyOfContextMap() {
        Map<String, String> hashMap = copyOnInheritThreadLocal.get();
        if (hashMap == null) {
            return null;
        } else {
            return new HashMap<>(hashMap);
        }
    }

    @Override
    public 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各种信息需要读者依据业务自行实现。。。
     */
    
    
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式全栈开发学习笔记---C语言笔试复习大全24

目录 内存管理 内存分配 堆和栈的区别&#xff1f;&#xff08;面试重点&#xff09; 申请内存的函数 malloc realloc free gcc工具链 编译的过程&#xff08;面试重点&#xff09; 第一步&#xff0c;预处理&#xff1a; 第二步&#xff0c;编译&#xff1a; 第三…

MySQL-数据库基础

一.MySQL安装 1.1卸载MySQL 把用户切换为root 查看是否有mysql数据库 ps axj | grep mysql 我这个是已经安装好的&#xff0c;为了更清楚的演示我把mysql关闭和mysql安装包卸载 关闭指令 systemctl stop mysqld查看是否在运行指令 systemctl stop mysqld查看安装包指令 …

校园招新之获取进QQ群但未报名人员

校园的社团、实验室招新一般由是校领导会发一个QQ通知&#xff0c;让各个班的同学们进一个招新群。 群里面会有负责人提示大家报名&#xff0c;但是群成员不总是都会报名&#xff0c;我们需要的就是&#xff0c;找到那些&#xff0c;已经进群&#xff0c;但是没有报名的同学&am…

网络原理 一

一、协议 网络通信中,协议是非常重要的概念. 协议进行了分层,此处就是按照这几层顺序来介绍每一层中的核心协议. 应用层,就对应着应用程序,是程序员打交道最多的一层,调用系统提供的 网络api 写出的代码都是基于应用层的. 应用层这里当然也有很多现成的协议,但更多的还是,程…

mysql实战——XtraBackup二进制包安装

1、二进制包下载网站 Software Downloads - Percona 2、安装xtrabackup 解压安装包 tar xvf percona-xtrabackup-8.0.27-19-Linux-x86_64.glibc2.17.tar.gz -C /usr/local 进入目录 cd percona-xtrabackup-8.0.27-19-Linux-x86_64.glibc2.17/ 安装依赖 yum install perl-Dig…

游戏子弹类python设计与实现详解

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、子弹类设计思路 1. 属性定义 2. 方法设计 三、子弹类实现详解 1. 定义子弹…

SimLab Composer v11.0.46 解锁版安装教程 (3D设计和逼真场景的多功能软件)

前言 SimLab Composer是由Simulation Lab公司推出的一款用于3D设计和逼真场景的多功能软件。该程序具有集成的图形环境&#xff0c;用于真实设计物理场景和对象&#xff0c;用户可以使用该软件中的工具设计简单到复杂和复杂。该程序的一个重要功能是能够构建和共享三维pdf文件…

揭秘章子怡成功之路:她是如何征服世界的?

章子怡的演艺生涯可谓是一部传奇❗❗❗ 从一个普通工人家庭的女孩&#xff0c;到如今的国际巨星 她的每一步都充满了努力和汗水 她的舞蹈基础为她日后的演艺事业奠定了坚实的基础 而她对戏剧和电影的热爱更是让她在演艺道路上不断前行 从《我的父亲母亲》到《卧虎藏龙》&…

【CTF Web】CTFShow web5 Writeup(SQL注入+PHP+位运算)

web5 1 阿呆被老板狂骂一通&#xff0c;决定改掉自己大意的毛病&#xff0c;痛下杀手&#xff0c;修补漏洞。 解法 注意到&#xff1a; <!-- flag in id 1000 -->拦截很多种字符&#xff0c;连 select 也不给用了。 if(preg_match("/\|\"|or|\||\-|\\\|\/|\…

Linux程序开发(十二):线程与多线程同步互斥实现抢票系统

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

window好用的网速工具

这是一个用于显示当前网速、CPU及内存利用率的桌面悬浮窗软件&#xff0c;并支持任务栏显示&#xff0c;支持更换皮肤。 github链接如下 https://github.com/zhongyang219/TrafficMonitor?tabreadme-ov-file

单窗口一天收益30+ 最新海外炼游地铁跑酷全自动黑科技项目,【软件+使用教程】

随着游戏界的最新狂潮&#xff0c;一款名为“海外酷跑”的游戏引起了全球玩家的关注。它继承了经典的地铁酷跑的精髓&#xff0c;同时注入了一种全新的元素&#xff1a;金币兑换虚拟货币的功能。玩家能够通过积极参与游戏、收集金币来兑换虚拟代币&#xff0c;每达到98000金币就…

详细分析Element中的MessageBox基本知识(附Demo)

目录 前言1. 基本知识2. Demo2.1 确认框2.2 警告框2.3 对话框 3. this.$confirm 前言 详细知识推荐阅读&#xff1a;详细分析Element Plus中的ElMessageBox弹窗用法&#xff08;附Demo及模版&#xff09; MessageBox则常用于Vue2 1. 基本知识 MessageBox 是 Element UI 提供…

每日一题——博弈论(枚举与暴力)

博弈论 题目描述 运行代码 #include<iostream> #include<vector> using namespace std; int main(){int n;cin >> n;vector<int> d(n,0);for(int i 0;i < n;i){cin >> d[i];}vector<int> in(1000,0);for(int k 1;k<3;k){for(int…

SpringBoot 集成 Nebula

工作需求&#xff0c;开始了解图数据库&#xff0c;经过工具选型&#xff0c;最终选择nebula graph&#xff0c;并集成到springboot&#xff0c;java 环境下如何对 Nebula Graph 进行操作&#xff0c;本文整理下过程。 1、首先引入 pom 依赖 <dependency><groupId&g…

【vue】el-select选择器实现宽度自适应

选择器的宽度根据内容长度进行变化 <div class"Space_content"><el-selectv-model"value":placeholder"$t(bot.roommessage)"class"select"size"small"style"margin-right: 10px"change"selectcha…

上5个B端系统的设计规范,让你的开发比着葫芦画瓢。

B端系统设计规范在企业级系统开发中起着重要的作用&#xff0c;具体包括以下几个方面&#xff1a; 统一风格和布局&#xff1a;设计规范能够统一系统的风格和布局&#xff0c;使不同功能模块的界面看起来一致&#xff0c;提升用户的使用体验和学习成本。通过统一的设计规范&am…

AI应用案例:影像报告智能辅助编辑系统

今天给大家介绍一个医疗行业的案例“影像报告智能辅助编辑系统”&#xff01;该案例已经在某三甲医院落地&#xff0c;模型准确度超过80%。 该项目上线后&#xff0c;保守估计&#xff0c;能为每位医生的每一张报告至少省下1分钟时间和2分钟的精力&#xff0c;20位初级医生&…

APP安全测试汇总【网络安全】

APP安全测试汇总 一.安装包签名和证书 1.问题说明 检测 APP 移动客户端是否经过了正确签名&#xff0c;通过检测签名&#xff0c;可以检测出安装包在签名后是否被修改过。如 果 APP 使⽤了 debug 进⾏证书签名&#xff0c;那么 APP 中⼀部分 signature 级别的权限控制就会失效…

材料物理 笔记-9

原内容请参考哈尔滨工业大学何飞教授&#xff1a;https://www.bilibili.com/video/BV18b4y1Y7wd/?p12&spm_id_frompageDriver&vd_source61654d4a6e8d7941436149dd99026962 或《材料物理性能及其在材料研究中的应用》&#xff08;哈尔滨工业大学出版社&#xff09; ——…