Redisson分布式锁底层实现原理

2023年 7月 14日 25.3k 0

在Java中有很多保证线程安全的方式,比如synchorized,lock锁等等,这些在单机环境下都能发挥不错的作用,但是在分布式的环境下,这些机制就会失去大部分的作用。

在分布式环境下就需要引入分布式锁,实现分布式锁的方式有好多种,比如redis、zookeeper,或者通过数据库来实现,但是在分布式的情况下还需要考虑机器宕机的情况,如果某台机器上的线程获取到了这个锁,但此时机器宕机了。那么就没办法去释放,就会造成死锁的情况。

为了避免这种情况,就需要给锁加上一个过期时间,而过期时间的设定又是一个令人非常头疼的问题。

在Redisson种有一个看门狗机制,它给出了一种过期时间的很好的解决办法。下面就来研究一下它具体实现吧。

Redisson的加锁入口是tryLock(),此方法需提供获取锁的等待时间,如果在规定时间内未抢到锁,会返回false。

image.png

这里可以看到tryLock()方法实际上是调用了下面这个方法,这里给了一个leaseTime的默认值,至于为什么是-1,我们接着往下看。

image.png

进来之后会发现,这个方法的核心就是执行一个tryAcquire方法,我们点进去看一下。

image.png

tryAcquire方法实际会去执行tryAcquireAsync异步的去获取锁,然后再使用get获取结果,如果结果为null代表获取锁成功,这里后面会讲。

image.png

然后进到tryAcquireAsync方法,在这里判断了leaseTime是不是-1,如果我们自己设定了过期时间,那么就会以我们设置的为准,并且不会去开启自动续期。

如果是默认的-1,那么异步获取锁之后,后面还会去开启一个自动续期的定时任务。

image.png

异步获取锁是通过tryLockInnerAsync这个方法实现的。第一个参数是30000,传入的是commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),点进去可以看到

image.png

在这个方法里使用lua脚本的方式去执行了set操作

这段lua脚本的意思是:

锁不存在,加锁成功,设置hash数据结构锁: 锁名 -> 加锁线程:id -> 加锁次数(1)
锁存在且是本线程的锁 加锁次数增加:锁名 -> 加锁线程:id -> 加锁次数+1
锁存在且不是本线程的锁 加锁失败 返回锁剩余过期时间

从这里也可以体现出此锁的可重入,即某一线程获取到锁之后,那么这个线程再去获取该锁的话也可以成功

同时也可以如果返回为null那么说明获取锁成功。

image.png

然后后面会判断如果结果为null,就会去执行scheduleExpirationRenewal(threadId)方法,进去看一下

image.png

由于我们的Redission的分布式锁是可重入锁,所以这里会首先判断一下是不是第一次加锁,如果不是第一次则加锁次数加 1 不会再开启续期 因为第一次加锁时调用

如果是第一次加锁的话就回去调用renewExpiraton()去开启自动续期。

image.png

addThreadId:重入次数+1

image.png

renewExpiraton()开启自动续期这个方法里面创建了一个定时任务,主要逻辑是通过renewExpirationAsync(threadId)方法去执行续期逻辑,执行成功后还会通过下面if (res) {renewExpiration();}方法递归调用。

注意到这个线程执行的间隔是internalLockLeaseTime / 3,也就是30 / 3 = 10s

image.png

我们可以看一下renewExpirationAsync方法里面的逻辑

此lua脚本的意思是:当前线程持有的锁是否还存在 存在的话重新设置锁的过期时间(默认 30 秒)

image.png

至此加锁的逻辑就追完了。

下面我们看一看释放锁的逻辑。其入口为:unlock方法,它会去调用unlockAsync方法。

image.png

unlockAsync里面掉了unlockInnerAsync方法去释放锁,

image.png

unlockInnerAsync方法点进去我们可以看到它也是通过lua脚本的方式去释放锁。

若锁不存在 返回
若锁存在 加锁次数 -1
若加锁次数仍不等于 0 (可重入),重新设置锁的过期时间,返回
若加锁次数减为 0,删除锁,同步发布释放锁事件,返回

image.png

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论