事务(八)
面对高并发是锁的实现要使用aop 实现,锁不能加在方法中,应为事务一般是方法结束后提交,而锁在finally 方法中提交,从而会出现锁已经解锁而事务还没来得及提交,下个锁获得到的数据就不对。
转载:https://www.cnblogs.com/xiohao/p/13996619.html
自己的结论:java代码中通过加锁控制并发,一定要在事务提交后释放锁,如果先释放并发代码锁,而事务还没有提交,那么下一个并发线程获取释放的锁,进入代码,查询数据就不对,因为上一个事务还没有提交。
所以并发锁与事务的配合有如下方案:
1.先对方法进行AOP加锁,在方法其中调用service的事务方法。即AOP并发锁控制的方法要包含事务方法。
2.对同一个方法加AOP并发锁以及事务@transactional注解(也是aop),这时两个aop控制同一个方法,就要注意事务的顺序问题,加锁的aop一定要在事务的aop之前,后面会讲解配置事务的前后顺序。
3.不通过aop进行加锁,直接通过代码手动加锁,手动释放锁,一定要保证先加锁、开启事务、提交/回滚事务、释放锁这样的顺序编写代码。
案例一
java事务锁_java中锁与@Transactional同时使用导致锁失效的问题
转载:https://blog.csdn.net/weixin_34614674/article/details/114053973
示例代码
@Transactional
public void update(int id) {
boolean lock = redisLock.lock(id);
if (!lock) {
throw new RuntimeException("当前人数过多,请稍后再试");
}
/*
业务代码在该区域
*/
redisLock.unlock(id);
}
问题分析
上面这个例子是无法保证数据的一致性.由于spring的aop,会在update方法之前开启事务,之后再加锁,当锁住的代码执行完成后,再提交事务,因此锁住的代码块执行是在事务之内执行的,可以推断在代码块执行完时,事务还未提交,锁已经被释放,此时其他线程拿到锁之后进行锁住的代码块,读取的库存数据不是最新的。
解决方法
我们可以在update方法之前就加上锁,在还没有开事务之前就加锁,那么就可以保证线程的安全性,从而不会出现脏读和数据不一致性等情况.
@RequestMapping("/execute")
public void execute(int id) {
boolean lock = redisLock.lock(id);
if (!lock) {
throw new RuntimeException("当前人数过多,请稍后再试");
}
service.update(id);
redisLock.unlock(id);
}
@Transactional
public void update(int id) {
/*
业务代码在该区域
*/
}
案例二
java业务相关的分布式锁在service锁住,如何在提交事务的时候同时解开锁
转载:https://ask.csdn.net/questions/993371
因为在controller进行锁,没有所需要的业务参数,从而在service加锁,然后service执行完成解锁。
就会存在没有提交事务而锁释放了,造成第二个请求进来数据脏读。有什么好的解决办法吗?
可以通过拆分一个Service的事务、锁的两部分工作,拆成2个Service,
Controller调用第一个Service(加锁、释放锁),第一个Service再调用第二个Service(事务控制部分)
示例,修改前
1 HelloController 4 --HelloService 7 事务开始 10 加锁 13 // 业务代码 16 解锁 19 事务结束
示例,修改后
1 HelloController 2 --LockService 3 加锁 4 --HelloService 5 事务开始 6 // 业务代码 7 事务结束 8 解锁