Java 当事物@Transactional跟锁synchronized同时出现时请注意

先看一段代码:代码逻辑很简单,当用户请求购买商品时我们去后台查找商品库存如果库存大于购买数量我们就更新库存。显然查找跟更新是一个事物所以我们要加@Transactional,同时为了防止高并发下多个用户同一时间去扣减库存也就是多个用户同时执行该方法就需要对方法加锁synchronized。看起来这段代码好像没啥问题,但是当你运行起来之后你会发现出现库存超扣现象
 @Transactional(rollbackFor = Exception.class)
    public synchronized Integer createOrder() throws Exception{
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product==null){
            throw new Exception("购买商品:"+purchaseProductId+"不存在");
        }

        //商品当前库存
        Integer currentCount = product.getCount();
        System.out.println(Thread.currentThread().getName()+"库存数:"+currentCount);
        //校验库存
        if (purchaseProductNum > currentCount){
            throw  new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
        }

        productMapper.updateProductCount(purchaseProductNum,"xxx",new Date(),product.getId());

        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");

        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItemMapper.insertSelective(orderItem);
        return order.getId();
    }
原因分析
@Transactional是通过aop的方式执行的,由于spring的aop,会在createOrder方法之前开启事务,之后再加锁,当锁住的代码执行完成后,再提交事务,因此锁住的代码块执行是在事务之内执行的,可以推断在代码块执行完时,事务还未提交,锁已经被释放,此时其他线程拿到锁之后进行锁住的代码块,读取的库存数据不是最新的
解决办法
通过分析我们知道,之所以会这样是应为事物并没有被锁住,所以要想达到想过我们必须先加锁在让事物生效,参考代码如下:
    public synchronized Integer createOrder() throws Exception{
        TransactionStatus transaction1 = platformTransactionManager.getTransaction(transactionDefinition);
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product==null){
            platformTransactionManager.rollback(transaction1);
            throw new Exception("购买商品:"+purchaseProductId+"不存在");
        }

        //商品当前库存
        Integer currentCount = product.getCount();
        System.out.println(Thread.currentThread().getName()+"库存数:"+currentCount);
        //校验库存
        if (purchaseProductNum > currentCount){
            platformTransactionManager.rollback(transaction1);
            throw  new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
        }

        productMapper.updateProductCount(purchaseProductNum,"xxx",new Date(),product.getId());
        platformTransactionManager.commit(transaction1);

        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        platformTransactionManager.commit(transaction);
        return order.getId();
    }
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值