Redis分布式锁实现Redisson 15问(二)

pivoteic
发布于 2022-6-17 16:57
浏览
0收藏

 

三、为什么需要设置加锁key的过期时间

 

通过上面的加锁逻辑可以知道,虽然我们没有手动设置锁的过期时间,但是Redisson默认会设置一个30s的过期时间,为什么需要过期时间呢?

 

主要原因是为了防止死锁。当某个客户端获取到锁,还没来得及主动释放锁,那么此时假如客户端宕机了,又或者是释放锁失败了,那么如果没有设置过期时间,那么这个锁key会一直在,那么其它线程来加锁的时候会发现key已经被加锁了,那么其它线程一直会加锁失败,就会产生死锁的问题。

 

四、如何自动延长加锁时间

 

通过上面的分析我们都知道,在加锁的时候,就算没有指定锁的过期时间,Redisson默认也会给锁设置30s的过期时间,主要是用来防止死锁。

 

虽然设置了默认过期时间能够防止死锁,但是这也有一个问题,如果在30s内,任务没有结束,但是锁已经被释放了,失效了,一旦有其它线程加锁成功,那么就完全有可能出现线程安全数据错乱的问题。

 

所以Redisson对于这种未指定超时时间的加锁,就实现了一个叫watchdog机制,也就是看门狗机制来自动延长加锁的时间。

 

在客户端通过tryLockInnerAsync方法加锁成功之后,如果你没有指定锁过期的时间,那么客户端会起一个定时任务,来定时延长加锁时间,默认每10s执行一次。所以watchdog的本质其实就是一个定时任务。

Redis分布式锁实现Redisson 15问(二)-鸿蒙开发者社区

最后会定期执行如下的一段lua脚本来实现加锁时间的延长。

Redis分布式锁实现Redisson 15问(二)-鸿蒙开发者社区

解释一下这段lua脚本中参数的意思,其实是跟加锁的参数的意思是一样

 

  • KEYS[1]:就是锁的名称,对于我们的demo来说,就是myLock
  • ARGV[1]:就是锁的过期时间
  • ARGV[2]:代表了加锁的唯一标识,b983c153-7421-469a-addb-44fb92259a1b:1。

 

这段lua脚本的意思就是判断来续约的线程跟加锁的线程是同一个,如果是同一个,那么将锁的过期时间延长到30s,然后返回1,代表续约成功,不是的话就返回0,代表续约失败,下一次定时任务也就不会执行了。

Redis分布式锁实现Redisson 15问(二)-鸿蒙开发者社区

注意:因为有了看门狗机制,所以说如果你没有设置过期时间(超时自动释放锁的逻辑后面会说)并且没有主动去释放锁,那么这个锁就永远不会被释放,因为定时任务会不断的去延长锁的过期时间,造成死锁的问题。但是如果发生宕机了,是不会造成死锁的,因为宕机了,服务都没了,那么看门狗的这个定时任务就没了,也自然不会去续约,等锁自动过期了也就自动释放锁了,跟上述说的为什么需要设置过期时间是一样的。

 

五、如何实现可重入加锁

 

可重入加锁的意思就是同一个客户端同一个线程也能多次对同一个锁进行加锁。

 

也就是同时可以执行多次 lock方法,流程都是一样的,最后也会调用到lua脚本,所以可重入加锁的逻辑最后也是通过加锁的lua脚本来实现的。

 

上面加锁逻辑的lua脚本的前段我上面已经说过,下半部分也就是可重入加锁的逻辑。

Redis分布式锁实现Redisson 15问(二)-鸿蒙开发者社区

下面这段if的意思就是,判断当前已经加锁的key对应的加锁线程跟要来加锁的线程是不是同一个,如果是的话,就将这个线程对应的加锁次数加1,也就实现了可重入加锁,同时返回nil回去。

 

可重入加锁成功之后,加锁key和对应的值可能是这样。

 

myLock:{

"b983c153-7421-469a-addb-44fb92259a1b:1":2


}

 

所以加锁lua脚本的第二段if的逻辑其实是实现可重入加锁的逻辑。

Redis分布式锁实现Redisson 15问(二)-鸿蒙开发者社区

文章转自公众号:三友的java日记

标签
已于2022-6-17 16:57:36修改
收藏
回复
举报
回复
    相关推荐