事务(八)

面对高并发是锁的实现要使用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             解锁

 

posted @ 2021-03-14 04:00  迷走神经  阅读(174)  评论(0编辑  收藏  举报