在某个项目中,需要使用mybatis-plus多租户功能以便数据隔离,前端将租户id传到后端,后端通过拦截器将该租户id设置到ThreadLocal以便后续使用,代码大体上如下所示:

ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
threadLocal.set(1);

我在Controller层使用线程池取了租户id,代码大体上如下所示:

ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
//获取租户id
});

这时候出问题了,出现了有时候取得到有时候取不到租户id的现象,但是经过若干次重试之后就能稳定获取到租户id;再次测试则发现如果前端传了其它的租户id,后端取得还是上一次获取到的租户id,这到底是为啥呢?

问题分析:首先,这里使用了InheritableThreadLocal为的就是实现父子线程传值,传了值也能取到,但是也不总是能取到,若干次之后就总是能取到了。看到这种现象,我们正常人的第一反应就是怀疑这里有缓存,每次使用的时候没有,使用完了就缓存起来,由于线程池在执行任务的时候并非总是使用同一条线程,当线程池中的核心线程全都缓存完了,再请求就稳定不报错了,然而有缓存的原因所以就算这时候外部请求换了一个租户id,线程池中的线程仍然使用的是老的租户id,这也是缓存最直接的体现。。。。。。这里纯属基于现象的个人猜测,并没有什么实锤,看官们谨慎驾驶,小心翻车。

那怎么解决该问题呢?

该问题产生的原因是InheritableThreadLocal的bug,至于什么bug,我也不清楚(笑),但是有解决方案,解决方案就是使用阿里的transmittable-thread-local 组件,github地址如下:https://github.com/alibaba/transmittable-thread-local 使用起来也非常简单

首先,引入maven依赖:

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

1. 改变ThreadLocal的创建方式

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// =====================================================

// 在父线程中设置
context.set("value-set-in-parent"); // ===================================================== // 在子线程中可以读取,值是"value-set-in-parent"
String value = context.get();

2.改变线程池创建方式

ExecutorService executorService = ...
// 额外的处理,生成修饰了的对象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);

也就是说除了正常创建线程池之外,还要对该线程池做一个代理。

就这么简单,搞完之后父子线程传数据就一切正常了。

ps. 个人觉得这里称呼"父子线程"并不妥当,因为线程池是系统启动之后就已经创建好了的,算了,钻牛角尖太没劲了。

使用 transmittable-thread-local 组件解决 ThreadLocal 父子线程数据传递问题的更多相关文章

  1. 解决threadLocal父子变量传递问题

    一.问题的提出 在系统开发过程中常使用ThreadLocal进行传递日志的RequestId,由此来获取整条请求链路.然而当线程中开启了其他的线程,此时ThreadLocal里面的数据将会出现无法获取 ...

  2. ThreadLocal父子线程之间的数据传递问题

    一.问题的提出 在系统开发过程中常使用ThreadLocal进行传递日志的RequestId,由此来获取整条请求链路.然而当线程中开启了其他的线程,此时ThreadLocal里面的数据将会出现无法获取 ...

  3. ThreadLocal父子线程传递实现方案

    介绍InheritableThreadLocal之前,假设对 ThreadLocal 已经有了一定的理解,比如基本概念,原理,如果没有,可以参考:ThreadLocal源码分析解密.在讲解之前我们先列 ...

  4. 使用ThreadLocal在线程内部传递数据

    最近在项目中使用到了JDK提供的线程池,遇到了在多线程环境下在线程内部共享数据的问题 使用ThreadLocal 来解决线程内部共享数据的问题 定义BO package com.unicom.uclo ...

  5. vue2.0父子组件以及非父子组件通信

    官网API: https://cn.vuejs.org/v2/guide/components.html#Prop 一.父子组件通信 1.父组件传递数据给子组件,使用props属性来实现 传递普通字符 ...

  6. ThreadLocal的坑--ThreadLocal跨线程传递问题

    1.父子线程间的传递问题 ThreadLocal的子类InheritableThreadLocal其实已经帮我们处理好了,通过这个组件可以实现父子线程之间的数据传递,在子线程中能够父线程中的Threa ...

  7. InheritableThreadLocal 在线程池中进行父子线程间消息传递出现消息丢失的解析

    在日常研发过程中,我们经常面临着需要在线程内,线程间进行消息传递,比如在修改一些开源组件源码的过程中,需要将外部参数透传到内部,如果进行方法参数重载,则涉及到的改动量过大,这样,我们可以依赖Threa ...

  8. InheritableThreadLocal类原理简介使用 父子线程传递数据详解 多线程中篇(十八)

      上一篇文章中对ThreadLocal进行了详尽的介绍,另外还有一个类: InheritableThreadLocal 他是ThreadLocal的子类,那么这个类又有什么作用呢?   测试代码 p ...

  9. vue 2.x之组件的数据传递(一)

    这是根据官方提供的脚手架vue-cli搭建,通过简单的案例来介绍vue数据的传递的方式,根据自己平时用到的,来做简单的总结: 1.父组件传递数据给子组件 父组件传递数据给子组件,需要把子组件引入,并挂 ...

随机推荐

  1. 【线程池】core与非core

    core和非core的区别: 当线程数小于core size时我们取task是这么取得: 小于时用poll并设置超时时间,否则用take阻塞等待 第二条件就出去了 没有拿到task的worker线程会 ...

  2. 【jvm】MinorGC和FullGC

    新生代GC(Minor GC)指发生在新生代的垃圾收集动作,因为java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快. 老年代GC(MajorGC/Full GC) ...

  3. 杂篇-之裸眼3D渲染相机及画面矫正

    主题公园类,或大型广场led屏幕,直角幕,三维影片如何制作,和最终画面如何矫正还原. 前两天,一朋友做的项目,大概就是一个柱状的led屏幕,可能是立在广场街角等处,如果这样子,那个柱状体的有颜色的两面 ...

  4. IdentityServer4系列 | 简化模式

    一.前言 从上一篇关于资源密码凭证模式中,通过使用client_id和client_secret以及用户名密码通过应用Client(客户端)直接获取,从而请求获取受保护的资源,但是这种方式存在clie ...

  5. 【Usaco 2009 Gold】JZOJ2020年9月19日提高B组T3 头晕的奶牛

    [Usaco 2009 Gold]JZOJ2020年9月19日提高B组T3 头晕的奶牛 题目 Description 奶牛们发现,在农场里面赛跑是很有趣的一件事.可是她们一旦在农场里面不断地转圈,就会 ...

  6. moviepy音视频剪辑:AudioClip的max_volume方法报TypeError: bad operand type for abs(): ‘list‘错

    ☞ ░ 前往老猿Python博文目录 ░ 一.环境 操作系统:win7 64位 moviepy:1.0.3 numpy:1.19.0 Python:3.7.2 二.应用代码及报错信息 应用代码 imp ...

  7. FFmpeg在Android Studio中断点调试

    一般情况下在Android平台使用FFmpeg为动态库或静态库的形式,只能通过设置FFmpeg日志回调来看一些FFmpeg输出的日志,有时需要debug来查看FFmpeg内部执行过程,本文记录一下在A ...

  8. Docker 基本概念(三)-生命周期详解(镜像、容器、仓库)

    Docker三大组件:镜像.容器.仓库.  一.镜像 1 从仓库获取镜像 #一.从仓库获取镜像,帮助命令:docker pull -help 命令:docker pull [选项] [docker R ...

  9. 云原生时代 给予.NET的机会

    .NET诞生于与Java的竞争,微软当年被罚款20亿美元. Java绝不仅仅是一种语言,它是COM的替代者! 而COM恰恰是Windows的编程模型.而Java编程很多时候比C++编程要容易的多,更致 ...

  10. .NET Core集成Seq+Serilog实现日志中心

    .NET Core集成Seq+Serilog实现日志中心 一,下载安装Seq https://datalust.co/download/all,版本很多,大家随便挑,开发版个人免费,商业版多账号需要收 ...