信息技术初二做网站,网站建设业务方法,免费网站建设阿里云,百度统计登录前言
不知大家是否有观察到一个最常见的错误#xff1a; 先开启事务#xff0c;然后针对资源加锁#xff0c;操作资源#xff0c;然后释放锁#xff0c;最后提交事务 你是否发现了在这样的场景下会出现并发安全的问题#xff1f; #xff08;提示#xff1a;一个线程A…前言
不知大家是否有观察到一个最常见的错误 先开启事务然后针对资源加锁操作资源然后释放锁最后提交事务 你是否发现了在这样的场景下会出现并发安全的问题 提示一个线程A在事务内部释放锁另一个线程B拿到了锁线程B看不到线程A的操作 导致 线程B 重复执行线程A已经对资源进行的操作
用一个业务场景去说明
用一个电商系统的订单处理场景来具体说明这个“事务没有完全被锁包住”会导致的问题。 需求 用户点击“下单”按钮后后台要 检查该用户是否已经提交过该订单防重复下单 如果没有就创建订单 并扣减库存 错误的业务代码
Transactional
public void createOrder(String userId, String orderNo) {if (!orderService.hasOrdered(orderNo)) {synchronized ((lock: orderNo).intern()) {// 锁内逻辑// 此处加锁}// 锁释放了但事务还没提交orderService.save(orderNo); // 保存订单productService.decreaseStock(); // 扣减库存}
}1、线程A 开启事务 查询是否已下单→ 查询不到因为数据库未提交 执行下单逻辑准备插入订单 锁在事务内部提前释放 事务还没提交 2、线程B 紧接着执行相同操作 开启事务 查询是否已下单→ 同样查询不到线程A没提交 执行下单逻辑插入重复订单、扣减库存 提交事务 最终结果
因为 数据库在**“读已提交**”隔离级别下线程B看不到线程A未提交的插入
又因为加锁只包了业务逻辑而不是整个事务范围
所以锁一旦提前释放线程B就能并发进来了 线程A和线程B都成功下了单
结果就是 重复支付 / 重复下单
改进
public void createOrderSafe(String userId, String orderNo) {synchronized ((lock: orderNo).intern()) { // 线程B被阻塞doCreateOrder(userId, orderNo); // 锁保护整个事务 内部是本地事务}
}
或者微服务中 使用redis分布式锁
RLock lock redissonClient.getLock(order: orderNo);
if (lock.tryLock()) {try {// 事务中执行订单判断与插入} finally {lock.unlock(); // 锁直到事务结束才释放}
}总结
锁放在事务外部