秒杀系统--多线程超卖现象解决方案

以下是秒杀系统防止超卖现象的产生

@RestController
public class IndexController {
    
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping("/deduct_stock")
    public String deductStock(){
    
    
        //防止超卖现象产生设置的锁
        String lockKey = "product_001";
        //用clientId标记加锁和释放锁是否是同一个线程完成的
        String clientId  = UUID.randomUUID().toString();
        //采用原子性操作,防止自己加的锁被别人释放了,这边就是防止运行中系统出现故障,锁没有释放,所以设置过期时间
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10, TimeUnit.SECONDS);

        if(!result){
    
     //这里的result结果相当于使用redis中的setnx指令得到的,假如redis数据库中有lockKey这个键值,说明有线程正在执行,则加锁失败
            return "服务器繁忙,请稍后再试";
        }

        try{
    
    
            /*如果没有上面的加锁机制,只有这句代码的话,当多个线程进行访问的时候,会出现超卖的情况;如果只是简单的使用synchronized这种重量级锁,
            只能应用在单机情况下,用户请求量大,需要用到分布式的时候则不能使用*/
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock > 0){
    
    
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock",realStock + "");
                System.out.println("扣减成功,剩余库存:" + realStock);
            }else{
    
    
                System.out.println("扣减失败,库存不足");
            }
        }
        //在finally解决线程执行过程中出现异常,可以将锁删除
        finally{
    
    
            //通过判断clientId是否相等来决定是否释放锁
            if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
    
    
                stringRedisTemplate.delete(lockKey);
            }
        }
        return "end";
        //还有看门狗机制,锁续命;防止程序还没有执行完,锁已经过期了
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39039885/article/details/121594200