常规流程
@Transactional(rollbackFor = Exception.class)
public Boolean pay(PayRequest payRequest) {
User dbUser = userService.findById(payRequest.getUserId());
UserPayAmount userPayAmount = new UserPayAmount();
userPayAmount.setUserId(payRequest.getUserId());
userPayAmount.setOrderId(payRequest.getOrderId());
userPayAmount.setBeforeAmount(dbUser.getAmount());
userPayAmount.setChangeAmount(payRequest.getPayAmount());
BigDecimal afterChageAmount = dbUser.getAmount().subtract(payRequest.getPayAmount());
userPayAmount.setAfterAmount(afterChageAmount);
dbUser.setAmount(afterChageAmount);
userService.update(newUser);
userPayAmountRepository.save(userPayAmount);
return true;
}
}
上面代码看上去没问题,但是存在很大的问题。
事务问题
dbUser.setAmount(afterChageAmount);
userService.update(newUser);
这段代码有问题。去掉userSer
并发下问题
使用Jmeter压一下。
初始值:10000
随机值:1~11
线程数:50
循环数:30
DELETE FROM user_pay_amount;
UPDATE `user` u set u.amount = 10000 WHERE id = 1;
SELECT user_pay_amount.before_amount,count(before_amount) FROM user_pay_amount GROUP BY before_amount;
SELECT count(DISTINCT(before_amount)) FROM user_pay_amount;

解决方案
锁住整个方法(有误)
实战
public synchronized Boolean pay(PayRequest payRequest) {
}
# 108 SELECT count(DISTINCT(before_amount)) FROM user_pay_amount;
,虽然数量增大,但是还是存在线程安全问题
原因
锁释放了,但是事务还未提交,下个线程查询到的还是旧的数据
spring事务使用的是aop,底层是动态代理。
https://blog.youkuaiyun.com/sinat_36454672/article/details/103353211
解决方法
去掉事务
public synchronized Boolean pay(PayRequest payRequest) {
dbUser.setAmount(afterChageAmount);
//去掉事务这里要显示调用
userService.update(newUser);
userPayAmountRepository.save(userPayAmount);
return true;
}
//最好这样写
User newUser = new User();
BeanUtils.copyProperties(dbUser, newUser);
newUser.setAmount(afterChageAmount);
userService.update(newUser);
去掉事务,结果正常

去掉事务后遗症
但是去掉事务,就无法保证用户余额是能被正确修改。
如下的事务起作用了么?
public Boolean payInTx(PayRequest payRequest) {
synchronized (this){
return pay(payRequest);
}
}
@Transactional
public Boolean pay(PayRequest payRequest) {
User dbUser = userService.findById(payRequest.getUserId());
if (dbUser.getAmount().compareTo(BigDecimal.ZERO) < 0) {
throw new RuntimeException("余额不足");
}
UserPayAmount userPayAmount = new UserPayAmount();
userPayAmount.setUserId(payRequest.getUserId());
userPayAmount.setOrderId(payRequest.getOrderId());
userPayAmount.setBeforeAmount(dbUser.getAmount());
userPayAmount.setChangeAmount(payRequest.getPayAmount());
BigDecimal afterChageAmount = dbUser.getAmount().subtract(payRequest.getPayAmount());
userPayAmount.setAfterAmount(afterChageAmount);
//如果事务起作用,那么应该还是150条,实际去重后是1,表示事务并没有起作用
dbUser.setAmount(afterChageAmount);
userPayAmountRepository.save(userPayAmount);
return true;
}
还是不行,事务起作用,但是锁没有起作用
@Transactional
public Boolean payInTx(PayRequest payRequest) {
synchronized (this){
return pay(payRequest);
}
}
@Transactional
public Boolean pay(PayRequest payRequest) {
dbUser.setAmount(afterChageAmount);
return true;
}
@Transactional
public Boolean payInTx(PayRequest payRequest) {
return pay(payRequest);
}
public synchronized Boolean pay(PayRequest payRequest) {
return true;
}
以上,事务生效,但是锁都没有作用
最终:
新建一个类,在外部使用锁,在内部使用事务。但是吞吐量比较少
外部锁方法
public class UserPayAmountManage {
private final UserPayAmountService userPayAmountService;
public synchronized Boolean synchronizedPay(PayRequest payRequest) {
return userPayAmountService.pay(payRequest);
}
}
内部事务
@Transactional
public Boolean pay(PayRequest payRequest) {
User dbUser = userService.findById(payRequest.getUserId());
if (dbUser.getAmount().compareTo(BigDecimal.ZERO) < 0) {
throw new RuntimeException("余额不足");
}
UserPayAmount userPayAmount = new UserPayAmount();
userPayAmount.setUserId(payRequest.getUserId());
userPayAmount.setOrderId(payRequest.getOrderId());
userPayAmount.setBeforeAmount(dbUser.getAmount());
userPayAmount.setChangeAmount(payRequest.getPayAmount());
BigDecimal afterChageAmount = dbUser.getAmount().subtract(payRequest.getPayAmount());
userPayAmount.setAfterAmount(afterChageAmount);
dbUser.setAmount(afterChageAmount);
// User newUser = new User();
// BeanUtils.copyProperties(dbUser, newUser);
// newUser.setAmount(afterChageAmount);
// userService.update(newUser);
userPayAmountRepository.save(userPayAmount);
return true;
}
本文探讨了一个Spring框架下关于事务管理和并发问题的案例,通过分析代码中潜在的问题,提出了解决方案,包括去除事务、使用外部锁与内部事务结合,以确保线程安全并提高吞吐量。
568

被折叠的 条评论
为什么被折叠?



