第一版
实现逻辑和基本原理
逻辑:
1、每一次访问进来都先去获得redis 锁 如果获得到 则继续执行,如果获取不到 则直接返回
2、redis 的key 设有过期时间 避免某个请求处理不当(或方法执行到一半宕机或网络原因)导致 redis key 不能正确释放 死锁
3 在 finally 方法里进行手工释放锁
基本原理(即有什么样的理论基础 才可以用redis做分布式锁):
1、setIfAbsent 即 setnx 当key不存在时设置成功,当key 存在时会设置失败
2、redis设置key 和 设置过期时间 必须为原子性
否则在设置完key 后系统宕机 此时还没来得及设置过期时间 那么这个可以就成了永久的key了 就会产生死锁的情况
setIfAbsent 不是原子性的 (稍后讲解决方案)
问题
1、当这个方法执行时间大于10秒 此时 方法还没有执行完 key就自动过期了 此时 第二个请求就可以进来了 ,实际上是不应该进来的。
2、接 第一条 如果第二条请求还没执行完 第一条请求执行完了并释放了锁 ,此时第三条请求可以顺利进来 ,如此循环 这个锁就失效了。
第二方案:
解决上述第二个问题:
1、为每个请求生成一个uuid
2、如果第二条请求还没执行完 第一条请求执行完了 然后进行判断 如果当前锁是自己的进行释放,如果当前锁不是自己的就不释放,这样就 避免了锁失效。
第一个问题暂时还没解决*********************************
对于 setIfAbsent 不是原子性的问题 可以使用lua 脚本去实现。
https://blog.csdn.net/LYQ20010417/article/details/123468307
public Map<String,0bject> getRedisLock(){
Map<String,0bject> map = new HashMap<>() ;
String uuid = UuID.randomUuID() .tostring();
//1.占分布式锁的同时 给锁设置过期时间。
//这是一个原子性操作,要么同时成功,要么同时失败。
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent( k: "lock",uuid, 30, TimeUnit.SECONDS);
//如果加锁成功
if (lock){
try
//执行业务逻辑map.put("msg","加锁成功");Jfinally {
//redis官网提供的解锁脚本,可实现 获取值和围除锁的操作是一个原子性操作
String script =
"if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
//2.使用函数执行副锁脚本
stringRedisTemplate,execute(new DefaultRedisScript<>(script,Long.class),Anrays.asList("lock"),uuid):
//加锁失败时,通过递归自旋重试。
else f
return this.getRedisLock() ;
return map;