一文详解Redisson分布式锁底层实现原理

 更新时间:2023年07月12日 09:29:08   作者:_Apricity  
这篇文章主要详细介绍了Redisson分布式锁底层实现原理,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

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

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

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

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

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

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

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

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

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

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

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

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

这段lua脚本的意思是:

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

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

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

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

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

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

addThreadId:重入次数+1

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

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

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

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

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

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

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

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

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

以上就是一文详解Redisson分布式锁底层实现原理的详细内容,更多关于Redisson分布式锁实现原理的资料请关注脚本之家其它相关文章!

相关文章

  • 后端java压缩图片超详细图文教程

    后端java压缩图片超详细图文教程

    这篇文章主要给大家介绍了关于后端java压缩图片的相关资料,片压缩是一种广泛采用的技术,它不仅能显著减小文件大小,释放更多存储空间,还能提升图片加载速度,避免长时间等待,需要的朋友可以参考下
    2024-04-04
  • java编写一个花名随机抽取器的实现示例

    java编写一个花名随机抽取器的实现示例

    这篇文章主要介绍了java编写一个花名随机抽取器的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Java实现DES加解密算法解析

    Java实现DES加解密算法解析

    这篇文章主要介绍了Java实现DES加解密算法解析,结合完整实例形式分析了DES加密的相关原理,需要的朋友可以参考下。
    2016-10-10
  • java线程池实战应用步骤详解

    java线程池实战应用步骤详解

    这篇文章主要介绍了java线程池实战应用小结,包括线程池的创建方式,本文给大家分享两种方式,结合实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2025-04-04
  • java应用程序如何自定义log4j配置文件的位置

    java应用程序如何自定义log4j配置文件的位置

    这篇文章主要介绍了java应用程序如何自定义log4j配置文件的位置,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java调用C++动态库超详细步骤讲解(附源码)

    Java调用C++动态库超详细步骤讲解(附源码)

    C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,这篇文章主要介绍了Java调用C++动态库的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • ThreadLocal工作原理及用法案例

    ThreadLocal工作原理及用法案例

    本文详细讲解了ThreadLocal工作原理及用法案例,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Java中枚举的使用方法详解

    Java中枚举的使用方法详解

    这篇文章主要介绍了Java中枚举的使用方法详解,比如我们想声明一组季节的集合,那这里面最多有四种,即春夏秋冬,不允许有其他的季节,那为了实现这种限制,体现出季节是固定的四个对象,我们可以使用枚举,需要的朋友可以参考下
    2023-07-07
  • kafka运维consumer-groups.sh消费者组管理

    kafka运维consumer-groups.sh消费者组管理

    这篇文章主要为大家介绍了kafka运维consumer-groups.sh消费者组管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • java实现贪吃蛇小游戏

    java实现贪吃蛇小游戏

    这篇文章主要为大家详细介绍了java实现贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07

最新评论