当前位置: 首页 > article >正文

Redisson分布式锁-锁的可重入、可重试、WatchDog超时续约、multLock联锁(一文全讲透,超详细!!!)

本文涉及到使用Redis实现基础分布式锁以及Lua脚本的内容,如有需要可以先参考博主的上一篇文章:Redis实现-优惠卷秒杀(基础版本)
在这里插入图片描述

一、功能介绍

(1)前面分布式锁存在的问题

  1. 在JDK当中就存在一种可重入锁ReentrantLock,可重入指的是在同一线程当中可以多次获取同一把锁;
  2. 之前实现的分布式锁是一种非阻塞式不可重试的锁,而在很多业务中第一时间获取锁失败是可以进行等待再重试的;
  3. 超时释放的逻辑不严谨,不可以简单设定一个超时时间即可;
  4. Redis的主从模式也可以理解为读写分离模式,也就是说Redis会有一个主节点与多个从节点,执行写操作时是访问主节点,执行读操作时访问的是从节点,同时需要主节点同步数据给从节点,保证主从数据一致性,而执行set获取锁操作就是一种写操作,当我们在主节点执行该动作时假如主节点宕机,没有完成数据同步,这时其他线程再从另一个节点上获取锁,可能就会出现线程安全问题。
    在这里插入图片描述

(2)Redisson介绍

"在Redis基础上实现的Java驻内存虚拟网格"意思是:在Redis基础上实现的一个分布式工具集合,也就是在分布式系统下可能用到的各种各样的工具。

在这里插入图片描述

二、可重入锁快速入门

在这里插入图片描述
在这里插入图片描述
tryLock方法是一个阻塞式动作,执行该方法就可以尝试去获取锁,而在设置的最大等待时间内,若发生获取锁失败,就会等待一小段时间并重试,在超过该最大等待时间后都没有拿到锁才会返回false,也就是一种重试机制
(1)代码改造
在这里插入图片描述
在这里插入图片描述

三、可重入锁的实现原理

在使用原来自行实现的分布式锁时,数据类型是String,仅可存放锁名称lock以及线程id的key-value数据值,不能实现可重入功能是因为我们获取锁主要依靠setnx命令,所以即便value中的线程id相同,也不能去重复set
在这里插入图片描述

要想实现可重入就可以参考JDK当中的可重入锁ReentrantLock的实现原理:简单来说就是在首次获取锁是使用setnx命令进行设置,在后续获取锁时若发现锁已存在则使用get命令去判断线程id是否相同,若相同则也可以成功取到,并且同时去记录锁的重入次数

那么现在使用的String类型数据结构就无法满足再去存放锁的重入次数的数据的要求,就可以改用hash类型
这种可重入锁的释放动作不能去直接删除整个数据,而是要去将重入次数减一,当重入次数减至0时就可以删除该锁,也就需要在每次释放锁时都去判断一下锁的重入次数是否为0了。
注意:hash类型的命令与先前String类型的命令不同,也就是不能直接使用setnx ex命令,需要去改变。

(1)完整执行流程

在这里插入图片描述
因为这里流程较为复杂,使用的Redis命令较多,为了保证逻辑的原子性就要使用Lua脚本来编写。

(2)Lua脚本编写

①获取锁

在这里插入图片描述

②释放锁

在这里插入图片描述

四、锁重试和WatchDog机制

在这里插入图片描述

(1)源码分析

①锁重试机制

在使用tryLock方法进行传参时发现同时存在两种实现方式,一种是直接指定超时时间与锁自动释放时间;一种是只指定超时时间
在这里插入图片描述
跟入tryLock内的tryAcquire获取锁方法
在这里插入图片描述
当我们调用tryLock方法时不去指定锁自动释放时间,只指定超时时间,那么在源码中就会自动为leaseTime值赋默认值为-1。
在该方法中首先会去判断leaseTime是否为-1,若不是则会去走默认获取锁的方法tryLockInnerAsync;若是则会借助getLockWatchdogTimeout方法 (WatchDog意为看门狗)来为该锁初始化一个超时时间–30s
在这里插入图片描述
在这里插入图片描述
跟入该tryLockInnerAsync方法,可以看到当获取锁成功时返回的是null,获取失败时返回的是锁的剩余时间ttl
在这里插入图片描述
获取到返回的锁剩余时间ttl后,若ttl不为null,那么就会去计算超时剩余时间,若时间仍有余则可以继续去尝试获取锁。

而在这里使用了subscribe订阅方法,用于订阅其他线程释放锁的信息,在Redis中的publish命令就是用于发布消息通知。

假设在超时剩余时间结束后还没有接收到锁释放通知,就会去取消订阅,并且返回false;
在这里插入图片描述
在这里插入图片描述
相反假设在超时剩余时间前接收到了锁释放通知,且超时剩余时间仍有余,就会再次去执行跟上面类似的重试获取锁的逻辑。在每次执行完这段逻辑后假设还没有获取到锁,但是超时剩余时间仍有余,那么就可以再次循环执行。
在这里插入图片描述

但是这里与上方的差别在于:这里采用的是监听信号量的方案,假设在其他线程中进行了锁释放,那么就会发出一个信号,并且在这边去尝试获取信号。

但是尝试获取信号也会存在一个最大等待时间,如果超过这个时间依然没有拿到锁则会返回false。我们也通过subscribeFuture这个Future对象来实现定时获取,也就是在等待指定时间后再去尝试获取锁,类似阻塞的原理,避免无效尝试,降低CPU消耗
在这里插入图片描述

②锁超时续约

假设我们获取锁成功并得到锁的剩余有效期ttl,但是此时有业务阻塞了,导致ttl到期,其他线程捕捉到这个信号就会立刻再去获取锁,那么就会出现线程安全问题了。也就是说我们必须要确定锁是因为业务执行完释放的,而不是因为阻塞释放
前面我们已经了解到当不直接指定锁超时时间时,会利用WatchDog看门锁机制来为该锁加上一个默认超时时间为30s。那么当获得到这个Future对象后,就会去判断锁的剩余有效期是否为null,若为null则会对锁剩余时间进行续约动作。
在这里插入图片描述
在这里插入图片描述

在该方法中每隔锁内部施放时间的三分之一 (internalLockLeaseTime / 3L,在这里可以理解为看门狗时间的三分之一,也就是30 / 3 = 10s),就会去自动刷新一次有效期。

在这里插入图片描述
也就是重置锁的有效期时间
在这里插入图片描述
因为在在方法该方法中又去递归调用自身,所以实现的是无限续约,也就可以理解为永不过期。
当锁释放时,才会去取消这个锁自动更新任务。
在这里插入图片描述
在这里插入图片描述

(2)总结

在这里插入图片描述

五、multLock

(1)主从一致性问题的产生原因

主从一致性导致的锁失效问题:
Redis的主从模式也可以理解为读写分离模式,也就是说Redis会有一个主节点与多个从节点,执行写操作时是访问主节点,执行读操作时访问的是从节点,同时需要主节点同步数据给从节点,保证主从数据一致性。而执行set获取锁操作就是一种写操作,当我们在主节点获取到锁后假如主节点发生宕机,没有完成数据同步,那么Redis的哨兵机制就会从其他从节点中选出一个新的主节点,但是这时从节点中没有该锁的数据,就相当于发生了锁失效,其他线程再来获取锁也是同样可以获取成功的,也就会出现线程安全问题。

在这里插入图片描述
Redis中解决该问题的思路:打破主从节点的思路,在每个节点上都保存该锁,并且当Java应用想要去获取锁时必须依次向每个节点都去获取锁,必须从每个节点处都能获取到锁、都保存了锁的标识,才算获取锁成功。
而且这种方案还同时保留了主从一致性机制,每个节点都可以去形成自己的主从关系,即便在一个主从节点上发生了不一致,只要其他两个节点上不发生问题那么最后都是可以健康运行的。

这套方案保留了主从一致性机制,确保了整个Redis集群的高可用特性,同时避免了主从一致性引发的锁失效问题。

在这里插入图片描述
所以multLock又称为"联锁"。

(2)代码实现

首先去准备多台Redis节点,并在Java应用中完成配置客户端
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注入三个Redis客户端并获取联锁
在这里插入图片描述
在这里插入图片描述
运行测试,发现在三个节点上都保存了锁
在这里插入图片描述

http://www.lryc.cn/news/2379695.html

相关文章:

  • Python爬虫实战:研究源码还原技术,实现逆向解密
  • WordPress Relevanssi插件时间型SQL注入漏洞(CVE-2025-4396)
  • Adobe Illustrator学习备忘
  • C#中的dynamic与var:看似相似却迥然不同
  • 求职困境:开发、AI、运维、自动化
  • 语言模型:AM-Thinking-v1 能和大参数语言模型媲美的 32B 单卡推理模型
  • ChatGPT:OpenAI Codex—一款基于云的软件工程 AI 代理,赋能 ChatGPT,革新软件开发模式
  • docker compose up -d 是一个用于 通过 Docker Compose 在后台启动多容器应用 的命令
  • 智能视觉检测技术:制造业质量管控的“隐形守护者”
  • 利用html制作简历网页和求职信息网页
  • Problem E: List练习
  • 卷积神经网络进阶:转置卷积与棋盘效应详解
  • 用 Kotlin 脚本(KTS)重塑 Android 工程效能:2000 字终极实践指南
  • 2025年5月13日第一轮
  • HarmonyOs开发之———使用HTTP访问网络资源
  • 小结:Android系统架构
  • 单物理机上部署多个TaskManager与调优 Flink 集群
  • 基于C#的MQTT通信实战:从EMQX搭建到发布订阅全解析
  • VUE3_ref和useTemplateRef获取组件实例,ref获取dom对象
  • ISP中拖影问题的处理
  • C++.备考知识点
  • SQLMesh 模型管理指南:从创建到验证的全流程解析
  • HarmonyOS AVPlayer 音频播放器
  • ⭐️白嫖的阿里云认证⭐️ 第二弹【课时1:提示词(Prompt)技巧】for 「大模型Clouder认证:利用大模型提升内容生产能力」
  • Filament引擎(一) ——渲染框架设计
  • c++从入门到精通(六)--特殊工具与技术-完结篇
  • JDK 1.8 全解析:从核心特性到企业实战的深度实践
  • MCP实战:在扣子空间用扣子工作流MCP,一句话生成儿童故事rap视频
  • 分布式微服务系统架构第134集:笔记1运维服务器经验,高并发,大数据量系统
  • 【SSL证书系列】客户端如何验证https网站服务器发的证书是否由受信任的根证书签发机构签发