一文详解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分布式锁实现原理的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot、Java 使用 Jsoup 解析 HTML 页面的详细步骤

    SpringBoot、Java 使用 Jsoup 解析 HTML 页面

    这篇文章主要介绍了SpringBoot、Java 使用 Jsoup 解析 HTML 页面的详细步骤,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • MapReduce实现TopN效果示例解析

    MapReduce实现TopN效果示例解析

    这篇文章主要为大家介绍了MapReduce实现TopN效果示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • java如何上传文件到sftp服务器

    java如何上传文件到sftp服务器

    本文主要讲述了如何使用JSch库将数据文件上传到SFTP服务器,并分享了过程中遇到的一些坑,例如目录创建、文件删除等问题,以及如何使用SFTP通道执行命令、文件上传等操作,通过本文,读者可以了解SFTP上传文件的基本步骤和注意事项,同时也提供了一些解决方法
    2026-04-04
  • 详解Spring FactoryBean灵活创建复杂对象的秘密武器

    详解Spring FactoryBean灵活创建复杂对象的秘密武器

    FactoryBean是Spring框架中用于创建复杂Bean的接口,通过编程方式控制Bean的创建过程,它允许开发者自定义Bean的创建逻辑,适用于集成第三方库、延迟初始化、动态代理和统一管理资源等场景,本文介绍Spring FactoryBean创建复杂对象的相关操作,感兴趣的朋友一起看看吧
    2025-02-02
  • SpringBoot使用AOP记录接口操作日志的方法

    SpringBoot使用AOP记录接口操作日志的方法

    日志记录量是很大的,所以只记录关键地方并按期归档,最好是存在如elasticsearch中,如果存在数据库中,分表是不错的选择,这篇文章主要介绍了SpringBoot使用AOP记录接口操作日志的方法,需要的朋友可以参考下
    2022-08-08
  • Spring Retry实现重试机制的示例详解

    Spring Retry实现重试机制的示例详解

    这篇文章主要为大家详细介绍了Spring-Retry的用法以及实现原理是怎么样的,文中的示例代码讲解详细,具有一定的参考价值,需要的可以了解一下
    2023-07-07
  • SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例

    SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例

    本文主要介绍了SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Java 将Excel转为UOS的操作方法

    Java 将Excel转为UOS的操作方法

    以.uos为后缀的文件,表示Uniform Office Spreadsheet文件,是一种国产的办公文件格式,该格式以统一办公格式(UOF)创建,使用XML和压缩保存电子表格,这篇文章主要介绍了Java 将Excel转为UOS,需要的朋友可以参考下
    2022-09-09
  • SpringMVC @RequestBody自动转json Http415错误的解决

    SpringMVC @RequestBody自动转json Http415错误的解决

    这篇文章主要介绍了SpringMVC @RequestBody自动转json Http415错误的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • java 中createStatement()方法的实例详解

    java 中createStatement()方法的实例详解

    这篇文章主要介绍了java 中createStatement()方法的实例详解的相关资料,需要的朋友可以参考下
    2017-06-06

最新评论