基于ReentrantLock锁解决超卖问题

@Service
@Slf4j
public class OrderService {
    
    

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderItemMapper orderItemMapper;
    @Resource
    private ProductMapper productMapper;
    //购买商品id
    private int purchaseProductId = 100100;
    //购买商品数量
    private int purchaseProductNum = 1;
    // 平台事务管理器
    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    // 事物的定义
    @Autowired
    private TransactionDefinition transactionDefinition;

    private Lock lock = new ReentrantLock();


//    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder() throws Exception{
    
    
        Product product = null;

        // 加锁了
        lock.lock();
        try {
    
    
            // 手动设置事物
            TransactionStatus transaction1 = platformTransactionManager.getTransaction(transactionDefinition);
            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);
        }finally {
    
    
            //释放锁
            lock.unlock();
        }

        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);
        // 手动提交事务
        platformTransactionManager.commit(transaction);
        return order.getId();
    }

}

主要方法是:

通过lock.lock();开启重入锁,在该锁下面开启事务,查询库存、校验库存以及增量更新库存,在库存为空或者库存不足时,需要跑出异常,使得事务可以回滚,最后提交事务。
在该锁块中需要使用try…finally…确保异常时,可以调用 lock.unlock();,使锁能够被释放。
然后再下面写订单的操作也需要开启一个新的事务。

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class DistributeDemoApplicationTests {
    
    
    @Autowired
    private OrderService orderService;

    @Test
    public void concurrentOrder() throws InterruptedException {
    
    
        Thread.sleep(60000);
        CountDownLatch cdl = new CountDownLatch(5);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);

        ExecutorService es = Executors.newFixedThreadPool(5);
        for (int i =0;i<5;i++){
    
    
            es.execute(()->{
    
    
                try {
    
    
                    // cyclicBarrier:加上之后,线程会在这里等待(等5个线程),然后一起执行
                   cyclicBarrier.await();
                    Integer orderId = orderService.createOrder();
                    System.out.println("订单id:"+orderId);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }finally {
    
    
                    cdl.countDown();
                }
            });
        }
        cdl.await();
        es.shutdown();
    }
}

通过cyclicBarrier.await();使得五个线程等待,然后并发执行,最后都要调用cdl.countDown();
在代码最后使用cdl.await();等待所有线程执行完毕,才将线程池关闭。

猜你喜欢

转载自blog.csdn.net/qq_31776219/article/details/113780380
今日推荐