分享一种非常常见的并发问题处理,先看下场景重现:
在这个并发场景中,有一下几个条件
- 使用了spring的@transactional注解处理事务,
- 在A方法调用了B方法,A和B在同一个service方法中,同时A和B方法都使用了
REQUIRED
的传播方式 - 在b方法中,先判断数据库中是否有记录,有则修改,无则添加。
好了,这个方法在使用jmeter测试时,就会有很大概率,无视b方法中的判断方法,往数据库中添加数据了。。
为什么呢,思考了一下,感觉有两个原因
- 两个线程同时进入B方法,则在查询数据库时都查询不到记录,自然都走添加记录的方法了。
- 第二个线程在查询时,第一个线程的事务还没有提交(因为使用
REQUIRED
的传播方式,导致线程1虽然数据insert的方法执行了,但是spring 事务并没有提交,还在等后续方法执行)。
我的解决方案主要记录如下
添加lock锁,在A方法开始加锁,在finally中解锁。示例代码在下面:
lock.lock();
try {
B b = applicationContext.getBean(B.class);
b.xxx(xxx);
} finally {
lock.unlock();
}
但是还不行,因为原因2,导致整体事务没提交,查询的时候还是查不到,所以,我将方法B的事务传播方法改成
REQUIRES_NEW
,使方法B在操作完数据库就立即提交,但是 方法A和B又在同一个service中,而在spring中,
如果A和B在统一service中,A直接调用B,则B的事务不起作用 ,所以我使用applicationContext.getBean(B.class)获取到b,再去调用b的xxx方法,测试,问题解决。