ThreadLocal与ForkJoin使用踩坑记录

由于并发的需要原因,使用CompletableFuture异步执行Dubbo接口,RpcContext中存储了tenantId等信息。上线一段时间后,发现有些时候拿到的上下文并不是自己线程的上下文。

  • 原因分析
    CompletableFuture.supplyAsync内部使用ForkJoinPool执行。
    要知道原因,需要了解forkjoin的原理,forkjoin其核心思想就是分而治之。使用递归的思想将一个大人物拆分为多个小任务,直到达到停止拆分的条件。
    在这里插入图片描述
    并且他每个线程(线程数默认为cpu数)都有一个无限的执行队列。线程会从执行队列里面取任务执行。并且执行过程中,如果某些线程执行的快,为了利用cpu,空闲的线程会偷取其他队列里面的线程,拿到自己队列并执行。当然,为了避免竞争,队列使用的是双向队列,自己线程从队列头获取任务,偷取任务从队列尾部获取。这里我找了一张图很好的描述了一下这个场景:
    在这里插入图片描述
    因此,部分情况下执行任务的线程不是保存了ThreadLocal信息的线程,而是窃取任务的线程。
  • 解决思路
    知道了原因,我们只需要修改ForkJoinPool,让它获取到正确的ThreadLocal信息。
    private static class SafeForkJoinPool extends ForkJoinPool {

        private static Field inheritableThreadLocalsField;
        private static Method createInheritedMapMethod;

        static {
            try {
                inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
                inheritableThreadLocalsField.setAccessible(true);
                createInheritedMapMethod = ThreadLocal.class.getDeclaredMethod("createInheritedMap", new Class[]{inheritableThreadLocalsField.getType()});
                createInheritedMapMethod.setAccessible(true);
            } catch (Exception e) {
                throw new ExceptionInInitializerError(e);
            }
        }

        public SafeForkJoinPool() {
        }

        public SafeForkJoinPool(int parallelism) {
            super(parallelism);
        }

        public SafeForkJoinPool(int parallelism,
                                ForkJoinPool.ForkJoinWorkerThreadFactory factory,
                                Thread.UncaughtExceptionHandler handler,
                                boolean asyncMode) {
            super(parallelism, factory, handler, asyncMode);
        }

        @Override
        public void execute(Runnable task) {
            //获取当前线程中的所有ThreadLocal数据
            Object parentLocals = getField(inheritableThreadLocalsField, Thread.currentThread());
            super.execute(() -> {
                Object locals = null == parentLocals ? null : invokeMethod(createInheritedMapMethod, null, new Object[]{parentLocals});
                //替换当前线程(包含窃取线程)中的所有ThreadLocal数据
                setField(inheritableThreadLocalsField, Thread.currentThread(), locals);
                task.run();
            });
        }

        private static Object invokeMethod(Method method, Object target, Object... args) {
            try {
                return method.invoke(target, args);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        private static Object getField(Field field, Object target) {
            try {
                return field.get(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        private static void setField(Field field, Object target, Object value) {
            try {
                field.set(target, value);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

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

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

相关文章

【EI会议/稳定检索】2024年电机与电气控制国际会议(ICMEC 2024)

2024 International Conference on Motor and Electrical Control 2024年电机与电气控制国际会议 【会议信息】 会议简称:ICMEC 2024 截稿时间:(以官网为准) 大会地点:中国厦门 会议官网:www.meciac.com 会议邮箱&…

夸张,腾讯实习三个月,存款20W+

大家好,我是白露。 今天在牛客上看到一条帖子,让我感叹万分:实习两三个月,竟然已经存下了20多万的存款。 这也太夸张了吧?不太真实啊…… 很多网友表示疑问,“两三个月实习顶多存两三万吧?武理…

【工具】windows下VMware17解锁mac安装选项(使用unlocker427)

目录 0.简介 1.环境 2.安装前后对比 3.详细安装过程 3.1 下载unlocker427 1)下载地址 2)下载unlocker427.zip 3)解压之后是这样的 4)复制iso中的两个文件到你本地的VMware的安装目录下 5)复制windows下的所有…

【笔记】从零开始做一个精灵龙女-装备阶段

这里只记录相对重要的步骤和一些思路 但是头发那块很详细哦~ (标的小数字不用在意,那个是我网课的时长记录) 耳环 1.创建一个圆环,调整参数 做好后再复制一个小的 肩甲 2.0-2.4 1.创建圆柱体/球体也可 然后把底部的两个点删…

有哪些好用的ai工具,可以提升科研、学习、办公等效率?

最近,Sora的诞生为AI再添了一把火。 据介绍,这款“文生视频”的Sora可以直接输出长达60秒的视频,并且包含高度细致的背景、复杂的多角度镜头,以及富有情感的多个角色。 不仅能准确呈现细节,还能理解物体在物理世界中…

Accelerate笔记:本地SGD

本地 SGD 是一种分布式训练技术,其中梯度不是每一步都同步。每个进程都会更新自己版本的模型权重,在给定的步数后,通过跨所有进程平均这些权重来同步它们。 在底层,本地 SGD 代码禁用了自动梯度同步(但累积仍然如预期工…

什么是最好的手机数据恢复软件?6 款手机数据恢复软件 [2024 年更新]

什么是最好的手机数据恢复软件?在这篇文章中,您将了解 6 款最好的免费手机数据恢复软件,并学习如何恢复数据的完整指南。 最好的手机数据恢复软件是什么? 手机数据恢复软件是恢复智能手机中丢失或删除的文件、消息、照片和其他宝…

Win10文件系统错误(-2147219196)

问题出现的原因: C盘快挤满了,导致电脑很卡,于是删掉了C盘用户下的一些文件C:\Users\DIY-PC,省了五六十G的内存,结果发现把一些系统文件也删掉了,导致图片预览报错 问题现象: (自…

6月软考新通知:24下集成大概率是中级蕞简单的一门

2024下半年软考6月新通知: 一、24下软考考试时间安排: 24下半年软考报名时间:8月19日-9月15日 24下半年软考考试时间:11月9-12日 24下半年软考成绩查询:12月中(预计) 二、考情分析 24上软考…

免费,C++蓝桥杯等级考试真题--第7级(含答案和解析)

C蓝桥杯等级考试真题--第7级 答案:D 解析:步骤如下: 首先,--a 操作会使 a 的值减1,因此 a 变为 3。判断 a > b 即 3 > 3,此时表达式为假,因为 --a 后 a 并不大于 b。因此,程…

气压、湿度、震动开关、声音、红外火焰传感器 | 配合Arduino使用案例

BMP180 气压传感器 BMP180 是一种用于测量气压的科学仪器。可以获取到温度、气压、海拔。 先在 arduino ide 中安装依赖 /****** Arduino 接线 ***** Arduino 传感器* VCC 5v* GND GND* A4 SDA * A5 SCL ***********************/#include &l…

【Springcloud微服务】MybatisPlus下篇

🔥 本文由 程序喵正在路上 原创,CSDN首发! 💖 系列专栏:Springcloud微服务 🌠 首发时间:2024年6月4日 🦋 欢迎关注🖱点赞👍收藏🌟留言&#x1f43…

Beyond Compare 4 代码对比重新激活使用30天

1.同时按住‘’Win”“R”键,打开运行窗口。 2.在文本框中输入“regedit”,然后点击“确定”。 3.打开注册表,删除相关注册信息 打开注册表后,依次点击左侧列的“HKEY_CURRENT_USER”-“SOFTWARE”-“Scooter Software”-“Beyo…

[预告] 现代C++之全面解读Mutex与RAII Lock

目标 在我们编写并发编程项目的时候,mutex是必须要掌握的点,深入mutex的底层原理与实现能够帮助我们更好的理解与使用mutex。例如:在编写代码时,我们会遇到如下几个场景: 如何避免死锁如何自动释放锁如何设置超时控制多…

KT142C语音芯片ic批量生产说明不需要usb拷贝音频

一、批量生产的简介 内置空间虚拟成U盘的批量生产说明,其实就是将音频文件配置文件打包成一个bin文件就可以了,当然借助于电脑端的exe工具。“FAT镜像文件生成工具_1.0.9.exe” 最后,将生成的文件,重命名为“userfat-日期-特点.b…

Digital Assets

目录 .HDA文件 Expanded directories .HDA文件 Houdini将数字资产存储于.hda文件内; .HDA文件格式是一种二进制存档格式(binary archive format),存储一个或多个资产的数据层级结构,包括资产节点的类型定义&#xf…

配置本地 apt 源

挂载iso镜像文件 注意:文章中的挂载方法是临时挂载,重启服务器失效 我是使用iBMC的虚拟控制台将我的iso文件以设备的形式挂载到服务器上,我的iso文件是设备:/dev/sr0 也可以直接将iso文件上传到服务器某个目录。 将 /dev/sr0 进…

手把手制作Vue3+Flask全栈项目 全栈开发之路实战篇 问卷网站(二)管理员后台

全栈开发一条龙——前端篇 第一篇:框架确定、ide设置与项目创建 第二篇:介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇:setup语法,设置响应式数据。 第四篇:数据绑定、计算属性和watch监视 第五篇 : 组件…

AI写作:AI助力内容创作,让你的工作效率翻倍

工欲善其事,必先利其器。 随着AI技术与各个行业或细分场景的深度融合,日常工作可使用的AI工具呈现出井喷式发展的趋势,AI工具的类别也从最初的AI文本生成、AI绘画工具,逐渐扩展到AI思维导图工具、AI流程图工具、AI生成PPT工具、AI…

海外仓出库系统:智能处理订单,增加海外仓货物流转率的关键所在

对海外仓来说,怎么才能提升效益?这应该是很多海外仓企业都在关心的问题。想提升海外仓的收益主要是三个大方向:开源、节流、提效。 所谓的开源,就是扩展业务类型和业务模式,在拓展新客户上下功夫。这是能让海外仓进来…