java锁方法无效_java并发编程之 service层处理并发事务加锁可能会无效

java并发编程之 service层处理并发事务加锁可能会无效

最近注意到一个问题--在service层处理要多次操作数据库事务时往往要@Transactional事务注解,这个时候就要注意了,如果是在并发情况下,而且在service层加了锁,这个时候并不能保证这个事务操作的原子性,并会出现我们意向不到的问题。

本人做了一个测试,service层有一个方法,方法中获取数据库中的一个int值,然后将这个值自增后存入数据库,例如下面两段代码:(注意方法上加了@Transactional事务注解)

获下方的写法,都是有问题的。

为了解决并发问题上述两段代码都用了不同的形式加了锁,可能大部分初级程序员都会觉得上述代码没有什么问题。但实际是两段代码都有问题。测试的时候用的是jmeter工具,用2000个线程同时访问模拟并发,经过多次测试后数据库中的值都小于2000。

b41ca32e87a3eebd53d0aad4352da1dd.png

那么和我们的想象不一致的原因是什么呢?

众所周知,我们在spring中使用@Transactional事务注解,那么这个事务的开启和提交是spring利用aop帮我们自动完成的。说白了就是执行方法之前spring帮我们开启事务,方法执行完毕后spring再帮我们把事务提交。而方法的执行和事务的开启及提交并不是一个原子操作。所以方法执行完毕以后事务并没有提交。所以我们无论是将synchronized关键字加再方法体上还是用代码块的方式,只能保证方法中的代码或synchronized代码块中的代码执行的原子性。所以在高并发的情况下,就极有可能出现一些线程service方法已经执行完毕或synchronized代码块已经执行完,但是事务还没有提交(数据库中的值并没有改变),另一些线程就开始读取数据库中的值(没有提交事务之前的值)那么就有可能多个线程读取的是同一个值。所以就会出现和我们预想的结果不一致的情况。所以在并发程序中,而且还牵扯到事务的情况下,要特别注意这一点。

解决办法

在controller层加锁。

在service层自己定义事务的开启和提交。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值