一文详解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项目开发中最容易犯的10个错误与解决

    Java项目开发中最容易犯的10个错误与解决

    作为 Java 开发,我们在写代码的过程中难免会产生各种奇思妙想的 bug ,有些 bug 就挺让人无奈的,下面小编就和大家详细介绍一下如何避免吧
    2026-03-03
  • Mybatis中通用Mapper的InsertList()用法

    Mybatis中通用Mapper的InsertList()用法

    文章介绍了通用Mapper中的insertList()方法在批量新增时的使用方式,包括自增ID和自定义ID的情况,对于自增ID,使用tk.mybatis.mapper.additional.insert.InsertListMapper包下的insertList()方法;对于自定义ID,需要重写insertList()方法
    2025-02-02
  • SpringBoot中使用@Scheduled注解创建定时任务的实现

    SpringBoot中使用@Scheduled注解创建定时任务的实现

    这篇文章主要介绍了SpringBoot中使用@Scheduled注解创建定时任务的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Springcloud Stream消息驱动工具使用介绍

    Springcloud Stream消息驱动工具使用介绍

    SpringCloud Stream由一个中间件中立的核组成,应用通过SpringCloud Stream插入的input(相当于消费者consumer,它是从队列中接收消息的)和output(相当于生产者producer,它是发送消息到队列中的)通道与外界交流
    2022-09-09
  • Java实现图片转base64完整代码示例

    Java实现图片转base64完整代码示例

    这篇文章主要给大家介绍了关于Java实现图片转base64的相关资料,Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法,需要的朋友可以参考下
    2023-12-12
  • Java随机字符串与简单加密工具类详解

    Java随机字符串与简单加密工具类详解

    这篇文章主要介绍了Java随机字符串与简单加密工具类,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Java实现将HTML文件与字符串转换为图片

    Java实现将HTML文件与字符串转换为图片

    在 Java 开发中,我们经常会遇到将 HTML 内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用 Free Spire.Doc for Java 库来实现这一功能吧
    2025-08-08
  • jdbc连SQL server显示1433端口连接失败图文解决方法

    jdbc连SQL server显示1433端口连接失败图文解决方法

    这篇文章主要给大家介绍了关于jdbc连SQL server显示1433端口连接失败的图文解决方法,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-06-06
  • SpringBoot如何使用RateLimiter通过AOP方式进行限流

    SpringBoot如何使用RateLimiter通过AOP方式进行限流

    这篇文章主要介绍了SpringBoot如何使用RateLimiter通过AOP方式进行限流,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Mybatis-Plus环境配置与入门案例分析

    Mybatis-Plus环境配置与入门案例分析

    MyBatis-Plus 是一个 Mybatis 增强版工具,在 MyBatis 上扩充了其他功能没有改变其基本功能,为了简化开发提交效率而存在,本篇文章带你配置环境并认识它
    2022-03-03

最新评论