基于线程和延迟队列实现的定时抢购

基于异步线程和延迟队列实现的定时抢购(粗糙版)

需求描述:

抢购开启后,指定时间内,结束抢购,并处理抢购结果。

  1. 获取开启抢购信息
  2. 倒计时
  3. 计时结束后,读取snap:product:XXX的剩余商品数量,不为0,则将剩余商品数量放置到商品余额中。
  4. snap:product:XXX设置为0(代表抢购结束),后续完善根据winners:product:XXX进行抢购订单的生成

设计实现:

思路: 利用延迟队列实现倒计时,利用异步线程每隔1S询问延迟队列状态。

@Configuration
public class DelayQueueConfig {

    @Bean
    public DelayQueue<DelaySnapMessage> delayQueue(){
        return new DelayQueue<DelaySnapMessage>();
    }
}
@Service
public class DelayService {
    private static final Logger LOGGER = LoggerFactory.getLogger(DelayService.class);
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private DelayQueue<DelaySnapMessage> delayQueue;

    public void put(String snapProductKey){
       delayQueue.put(new DelaySnapMessage(snapProductKey,30, TimeUnit.SECONDS));
    }

    public void put(String snapProductKey, long snapTime){
        delayQueue.put(new DelaySnapMessage(snapProductKey, snapTime, TimeUnit.SECONDS));
        LOGGER.info("抢购开始时间:{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm:ss")));

    }
    
    @Async   //异步执行,在Application类也需要添加该注解
    @PostConstruct  //该方法在注入为bean之前会先执行一次。
    protected void handleDelay(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info("抢购延迟处理线程开始执行时间:{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm:ss")));
                int i = 0;
                while (i < 12){
                    DelaySnapMessage message = delayQueue.poll();

                    if(message != null){
                        String snapProductKey = message.getSnapProductKey();
                        LOGGER.info("抢购结束时间:{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm:ss")));
                        int productNum = (Integer) redisTemplate.opsForValue().get(message.getSnapProductKey());
                        if(productNum > 0){
                            LOGGER.info("抢购商品 {},剩余数量:{}",snapProductKey,productNum);
                        }
                        redisTemplate.opsForValue().set(snapProductKey,0);

                        //将剩余的库存返回到MySQL中
                        Long productId = GetRedisKey.getProductIdBySnapKey(snapProductKey);
                        Product product = productMapper.selectByPrimaryKey(productId);
                        product.setInventory(new Long(product.getInventory() + productNum));
                        productMapper.updateByPrimaryKey(product);
                    }

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                LOGGER.info("抢购延迟处理线程结束执行时间:{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm:ss")));
            }
        }).start();

    }
}

总结

优点:

直接调用JDK,不适用额外中间件,依赖简单。

缺点:

使用异步线程进行数据库更新,存在数据库同步隐患。(设计缺陷)

延迟队列,需要持续询问(poll() )效率很低。

目前设计实现是每1S询问一次,存在抢购结束时间1S的误差。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值