秒杀场景的线程安全问题

来源:2-3 章节思考题

qq_往事_8

2022-04-05

一哥好,我在自己编写代码模拟秒杀场景时如果一秒内请求线程数多的话,就会出现订单生成的数量和请求数相同,但是库存却没扣完的情况(比如:1000库存、1010线程请求,请求结束后库存还剩好几百 订单数量是1010)。又或者是库存扣没了,但是订单数量却不够。下面是我写的代码,帮忙看看问题出现在哪里呗,除了加锁之外还能有什么方法吗。

/**
     * 扣减优惠券数量
     *
     * @param id 优惠券id
     */
    @Override
    public void deductStock(String id) {
        log.info("deduct stock: [{}]", id);

        QueryWrapper<SeckillVoucher> queryWrapper = new QueryWrapper<SeckillVoucher>()
                .eq("voucher_id", id);
        SeckillVoucher seckillVoucher = getOne(queryWrapper);

        if (seckillVoucher == null) {
            log.error("can not queried seckill voucher: [{}]", id);
            throw new CommonException("can not queried seckill voucher " + id);
        }

        LocalDateTime now = LocalDateTime.now();
        if (!(seckillVoucher.getBeginTime().isBefore(now)
                && seckillVoucher.getEndTime().isAfter(now))) {
            throw new CommonException("not in activity time");
        }

        if (seckillVoucher.getStock() < 1) {
            throw new CommonException("current stock not enough");
        }

        seckillVoucher.setStock(seckillVoucher.getStock() - 1);
        UpdateWrapper<SeckillVoucher> updateWrapper = new UpdateWrapper<SeckillVoucher>()
                .eq("voucher_id", id).gt("stock", 0);

        boolean isSuccess = update(seckillVoucher, updateWrapper);

        if (!isSuccess) {
            throw new CommonException("deduct stock failure");
        }

        log.info("update seckill voucher success: [{}]", JSON.toJSONString(seckillVoucher));
    } 
/**
     * 创建优惠券购买记录
     *
     * @param id 优惠券id
     */
    @Override
    @Transactional
    public void createVoucherOrder(String id) {
        log.info("create voucher order: [{}]", id);

        seckillVoucherService.deductStock(id);

        UserDTO user = UserHolder.getUser();

        VoucherOrder newVoucherOrder = new VoucherOrder();
        newVoucherOrder.setId(idWorker.nextId("order"));
        newVoucherOrder.setUserId(user.getId());
        newVoucherOrder.setVoucherId(id);
        newVoucherOrder.setPayType(1);  // 余额支付
        newVoucherOrder.setPayTime(LocalDateTime.now());

        save(newVoucherOrder);
    }
写回答

1回答

qq_往事_8

提问者

2022-04-05

找到问题了

0
0

Spring Cloud / Alibaba 微服务架构实战

从架构设计到开发实践,手把手实现

1210 学习 · 674 问题

查看课程