高并发情况下使用动态多数据源dynamic-datasource 3.1.0并发问题复盘
最近公司内某个项目出现了因并发问题导致的多数据源串掉的问题,经研究找到了如下解决方案
复现流程
A线程切换指定数据源并挂起
B线程使用了默认数据源,A线程先于B线程,结果B线程使用了从库数据源(health_bi)
问题原因
DynamicDataSourceContextHolder中NamedInheritableThreadLocal导致两个数据源共用同一数据源队列Deque<String> 且NamedInheritableThreadLocal会出现将此子线程的值复制到主线程中
InheritableThreadLocal的类注释
This class extends ThreadLocal to provide inheritance of values from parent thread to child thread: when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values. Normally the child's values will be identical to the parent's; however, the child's value can be made an arbitrary function of the parent's by overriding the childValue method in this class.
Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.
经翻译为
此类扩展ThreadLocal以提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承线程本地变量的初始值。通常情况下,孩子的值与父母的值相同;但是,通过重写该类中的childValue方法,可以使子级的值成为父级的任意函数。
当变量中维护的每线程属性(例如,用户ID、事务ID)必须自动传输到创建的任何子线程时,可继承线程本地变量优先于普通线程本地变量。
子线程创建的时候会获取所有父线程的值。而子线程的值又会传递给父线程,所以就相当于所有子线程的值是共享的,这个问题才会出现。而跟代码的时候你会发现其实所有线程的ArrayDeque都是指向同一个地址
当用new Thread()创建线程时,会走下面的代码,那么inheritThreadLocals就会是true
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
解决方案
升级dynamic-datasource最新版本,使用NamedThreadLocal,NamedThreadLocal并无其他操作仅仅是命名
参考资料
相关文章
- Java要抛弃祖宗的基业,Java程序员危险了!
- 十大 Java 语言特性
- JVM 三色标记算法,原来是这么回事!
- 聊聊 Spring 事务控制策略以及 @Transactional 失效问题避坑
- 写给 Java 程序员的前端 Promise 教程
- 写给 Java 程序员的前端 Promise 教程,你学会了吗?
- Java 中为什么不全部使用 Static 方法?
- Java 池化技术你了解多少?
- Java 服务 Docker 容器化优秀实践
- Spring Boot + EasyExcel导入导出,简直太好用了!
- 我们一起聊聊 Java 内存泄漏
- CentOS 下安装 Docker 极简教程
- JDK 19 功能集冻结:Java 19 只有七个新特性
- 关于 CMS 垃圾回收器,你真的懂了吗?
- 为什么会有这么多编程语言?
- 改善Java代码的八个建议
- 接口流量突增,如何做好性能优化?
- Java 以编程方式创建JAR文件
- POJO、Java Bean是如何定义的
- Spring 的 Bean 明明设置了 Scope 为 Prototype,为什么还是只能获取到单例对象?