Spring事务中含有Synchronized锁会发生什么?
问题描述:
在学习线程的时候,sleep()不会释放锁,只能在Synchronized方法块执行完的时候释放。于是想到自己之前写的一段代码,在@Transactional()下的方法中写入了一个Synchronized方法块,会不会出问题?
业务场景是这样,发放优惠券10000张,为了避免在领取优惠券的时候出现线程安全问题,没有使用悲观锁和乐观锁,就加入了Synchronized方法块。
废话不多说,直接上代码。
100个测试线程:
Service层代码:
sql代码(没有用悲观锁和乐观锁):
测试前结果:
测试结果:
你没看错,执行之后数量只增加了9,那么其他91条哪儿去了呢?为什么会是这样的结果?
下面是部分日志打印:
最后执行sql日志打印:
由日志可见这些sql的日志执行是没有顺序,虽然已经到524,但是最后执行的还是519,所以我们所见的结果还是519。
问题出在哪里?是不是由于锁在事务中,在事务还没有commit的时候,下一个线程就已经获取了锁,从而导致读取的是commi之前,数据库的数据?
把事务注解去掉测试一下:
测试结果:
结果是对的,再看日志打印:
可见,这是一个有序的执行过程,结果正确;
解决方案:
显然,上述方式不足以解决我们的问题,但是我们知道,在事务中使用锁,其实并不能保证线程安全,那么在锁中使用事务呢?
修改方案如下:
service代码:
执行结果:
执行结果正确,再看日志打印:
执行也是有序的。之前的错误数据是在结束了Synchronized方法块之后但是在提交之前,别的线程读取了数据,然后最后执行的提交数据决定数据的结果。之后写代码还是要避免这种情况,还是要多想想,在代码中发生的细节过程,避免出现bug。