以下是秒杀系统防止超卖现象的产生
@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";
//还有看门狗机制,锁续命;防止程序还没有执行完,锁已经过期了
}
}