这次我们来简单说说分布式锁,我记得过去我也过一篇JMM的内存一致性算法,就是说拿到锁的可以继续操作,没拿到的自旋等待。

思路与场景

  我们在Zookeeper中提到过分布式锁,这里我们先用redis实现一个简单的分布式锁,这里是我们一个简单的售卖减库存的小实例,剩余库存假设存在数据库内。

复制代码
@GetMapping(value = "/getLock") public String getLock() {     int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));     if (stock > 0) {         int realStock = stock - 1;         stringRedisTemplate.opsForValue().set("stock", realStock + "");         System.out.println("售卖成功,剩余" + realStock + "");         return "success";     }else{         System.out.println("剩余库存不足");         return "fail";     } }
复制代码

  这样简单的实现了一个售卖的过程,现在看来确实没什么问题的,但是如果是一个并发下的场景就可能会出现超卖的情况了,我们来改造一下代码。

复制代码
@GetMapping(value = "/getLock") public String getLock() {     synchronized (this) {         int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));         if (stock > 0) {             int realStock = stock - 1;             stringRedisTemplate.opsForValue().set("stock", realStock + "");             System.out.println("售卖成功,剩余" + realStock + "");             return "success";         } else {             System.out.println("剩余库存不足");             return "fail";         }     } }
复制代码

  貌似这回就可以了,可以抗住高并发了,但是新的问题又来了,我们如果是分布式的场景下,synchronized关键字是不起作用的啊。也就是说还是会出现超卖的情况的啊,我们再来改造一下

复制代码
@GetMapping(value = "/getLock") public String getLock() {     String lockKey = "lock";      Boolean bool = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "xiaocai");//相当于我们的setnx命令    if(!bool){         return "error";     }      int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));     if (stock > 0) {         int realStock = stock - 1;         stringRedisTemplate.opsForValue().set("stock", realStock + "");         System.out.println("售卖成功,剩余" + realStock + "");         stringRedisTemplate.delete(lockKey);         return "success";     } else {         System.out.println("剩余库存不足");         stringRedisTemplate.delete(lockKey);         return "fail";     } }
复制代码

  这次我们看来基本可以了,使用我们的setnx命令来做一次唯一的限制,万一报错了呢?解锁怎么办?再来改造一下。