高并发二(秒杀系统-—乐观锁、悲观锁、Synchronized)

一、情景

某件商品(库存只有100件),如何防止在秒杀活动中被超卖的问题(500的并发)

二、不加锁

   @ApiOperation("下单")
    @RequestMapping(value = "/createWrongOrder/{stockId}", method = RequestMethod.GET)
    @ResponseBody
    public String createWrongOrder(@PathVariable String stockId) {
        return orderService.createWrongOrder(stockId);
    }
 /**
     * 库存100,并发500 下同
     * 结果:不正常
     * 库存扣50,下单500
     */
    @Override
    @Transactional
    public String createWrongOrder(String stockId) {
        //校验库存
        Stock stock = checkStock(stockId);
        //扣库存
        saleStock(stock);
        //创建订单
        return createOrder(stock);
    }
  private Stock checkStock(String stockId) {
        Stock stock = stockDao.findById(stockId).orElseThrow(() -> new NotFoundException("商品不存在"));
        if (stock.getSale() >= stock.getCount()) {
            throw new BadRequestException("库存不足");
        }
        return stock;
    }

   private void saleStock(Stock stock) {
        stock.setSale(stock.getSale() + 1);
        stock.setUpdateTime(new Date());
        stockDao.save(stock);
    }

   private String createOrder(Stock stock) {
        StockOrder order = new StockOrder();
        order.setStockId(stock.getStockId());
        order.setName(stock.getName());
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        StockOrder stockOrder = stockOrderDao.save(order);
        return "ID:" + stockOrder.getStockOrderId() + " == Name:" + stockOrder.getName();
    }

三、乐观锁

   @ApiOperation("下单-乐观加锁")
    @RequestMapping(value = "/createOptimisticOrder/{stockId}", method = RequestMethod.GET)
    @ResponseBody
    public String createOptimisticOrder(@PathVariable String stockId) {
        return orderService.createOptimisticOrder(stockId);
    }
    /**
     * 库存100,并发500 下同
     * 结果:正常
     * 库存扣51,下单51
     */
    @Override
    @Transactional
    public String createOptimisticOrder(String stockId) {
        //校验库存
        Stock stock = checkStock(stockId);
        //乐观锁扣库存
        saleStockOptimistic(stock);
        //创建订单
        return createOrder(stock);
    }
    private void saleStockOptimistic(Stock stock) {
        int count = stockDao.updateByOptimistic(stock.getSale() + 1, stock.getStockId(), stock.getVersion());
        if (count == 0) {
            throw new BadRequestException("并发更新库存失败,version不匹配");
        }
    }

    @Modifying
    @Query("update Stock set sale = ?1 ,version = version+1 where stockId = ?2 and version = ?3")
    int updateByOptimistic(Integer sale, String stockId, Integer version);

四、悲观锁

  @ApiOperation("下单-悲观加锁")
    @RequestMapping(value = "/createPessimisticOrder/{stockId}", method = RequestMethod.GET)
    @ResponseBody
    public String createPessimisticOrder(@PathVariable String stockId) {
        return orderService.createPessimisticOrder(stockId);
    }
 /**
     * 库存100,并发500 下同
     * 结果:正常
     * 库存扣100,下单100
     */
    @Override
    @Transactional
    public String createPessimisticOrder(String stockId) {
        //悲观锁校验库存
        Stock stock = checkPessimisticStock(stockId);
        //扣库存
        saleStock(stock);
        //创建订单
        return createOrder(stock);
    }
 private Stock checkPessimisticStock(String stockId) {
        Stock stock = stockDao.findByStockIdAndPessimistic(stockId);
        if (stock.getSale() >= stock.getCount()) {
            throw new BadRequestException("库存不足");
        }
        return stock;
    }

   @Lock(value = LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT stock FROM Stock stock WHERE stock.stockId= ?1")
    Stock findByStockIdAndPessimistic(String stockId);

五、Sync加锁失效

  @ApiOperation("下单-Sync加锁失效")
    @RequestMapping(value = "/createSynchronizedOrder/{stockId}", method = RequestMethod.GET)
    @ResponseBody
    public String createSynchronizedOrder(@PathVariable String stockId) {
        return orderService.createSynchronizedOrder(stockId);
    }
 /**
     * 库存100,并发500 下同
     * 结果:不正常
     * 库存扣51,下单500
     */
    /**
     * 由于Spring事务是通过AOP实现的,所以在 createSynchronizedOrder 方法执行之前会有开启事务,之后会有提交事务逻辑。
     * 而synchronized代码块执行是在事务之内执行的,可以推断在synchronized代码块执行完时,事务还未提交,其他
     * 线程进入synchronized代码块后,读取的库存数据不是最新的。
     *
     * @param stockId
     * @return
     */
    @Override
    @Transactional
    public synchronized String createSynchronizedOrder(String stockId) {
        //校验库存
        Stock stock = checkStock(stockId);
        //扣库存
        saleStock(stock);
        //创建订单
        return createOrder(stock);
    }

六、Sync加锁成功

  @ApiOperation("下单-Sync加锁成功")
    @RequestMapping(value = "/createSynchronizedOrderV2/{stockId}", method = RequestMethod.GET)
    @ResponseBody
    public String createSynchronizedOrderV2(@PathVariable String stockId) {
        return orderService.createSynchronizedOrderV2(stockId);
    }
 /**
     * 库存100,并发500 下同
     * 结果:正常
     * 库存扣100,下单100
     */
    @Override
    public synchronized String createSynchronizedOrderV2(String stockId) {
        return (SpringUtil.getBean(this.getClass())).createSynchronizedOrder(stockId);
    }

七、源码

https://github.com/akeung/akeung_learning.git

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章