单体系统一人一单并发问题总结
1.悲观锁
- 添加同步锁,让线程串行化执行(性能一般)
- synchronize、lock
2.乐观锁
- 不加锁,在更新时判断是否有其他线程在修改
- 性能好,但存在成功率低的问题
- CAS自旋锁
- 添加版本号(version)解决A-B-A问题 -----> 1A-2B-3C
知识点随记:
1.spring框架事务失效
2.aop代理对象
3.synchronized锁对象
问题分析
需求:只有同一用户访问才考虑并发问题
- synchronized如果直接加在方法上,锁住的是当前对象,等于所有人访问都会加锁
@Transactional
public synchronized Result createVoucherOrder(Long voucherId){
//一人一单
Long userId = UserHolder.getUser().getId();
....
....
....
return Result.ok()
}
- 改进一:缩小范围,对用户id进行加锁
问题:当执行到方法的最后一行代码时,synchronized锁就会释放,但是现在事务还未提交,如果现在其他线程进来还是会出现并发问题,因为事务没有提交,数据库就没有得到更新
@Transactional
public Result createVoucherOrder(Long voucherId) {
//一人一单
Long userId = UserHolder.getUser().getId();
//这里将userId转string后每次都会是一个新的对象
//调用intern方法后他取的是string后的值进行比较
synchronized (userId.toString().intern()) {
....
....
....
}
return Result.ok()
}
- 改进二:放方法调用前加锁,实现事务先提交,再释放锁
问题:这里调用createVoucherOrder方法的对象是当前对象
1)事务生效:是因为spring对当前类进行了动态代理,使用代理对象对事务进行处理
2)这里的this是目标对象,属于非代理对象,所有这里是没有事务功能的,@Transactional会失效
@Override
public Result secKillVoucher(Long voucherId) {
...
...
...
Long userId = UserHolder.getUser().getId();
//2.再释放锁
synchronized (userId.toString().intern()) {
//1.先提交事务
return this.createVoucherOrder(voucherId);
}
}
@Transactional
public Result createVoucherOrder(Long voucherId) {
//一人一单
Long userId = UserHolder.getUser().getId();
....
....
....
return Result.ok()
}
解决事务失效问题:
- 我们需要拿到代理对象
- 用代理对象去调用方法
- 记得加aspectjweaver依赖
- 记得在启动类开启AOP自动代理并暴露 EnableAspectJAutoProxy(exposeProxy = true)
- 完美解决
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
@Override
public Result secKillVoucher(Long voucherId) {
...
...
...
Long userId = UserHolder.getUser().getId();
//2.再释放锁
synchronized (userId.toString().intern()) {
//1.先提交事务
//获取当前对象的代理对象
IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
//proxy是由spring容器管理的,用proxy调用方法事务就会生效
return proxy.createVoucherOrder(voucherId);
}
}
@Transactional
public Result createVoucherOrder(Long voucherId) {
//一人一单
Long userId = UserHolder.getUser().getId();
....
....
....
return Result.ok()
}