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

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客户端并获取联锁
在这里插入图片描述
在这里插入图片描述
运行测试,发现在三个节点上都保存了锁
在这里插入图片描述

相关文章:

  • Qt窗口中消除边框的解决方法
  • HarmonyOS 开发之 —— 合理使用动画与转场
  • 【第七节】ESP32-S3 霍尔传感器应用实战:磁场检测与蜂鸣器控制
  • 消息扩散--tarjan缩点
  • 反转链表链表数据结构oj题(206)
  • 【未完】【GNN笔记】EvolveGCN:Evolving Graph Convolutional Networks for Dynamics Graphs
  • 测试:TestCafe - 判断按钮是否活性化
  • 【Elasticsearch】flattened`类型在查询嵌套数组时可能返回不准确结果的情况
  • 电子电路仿真实验教学平台重磅上线!——深圳航天科技创新研究院倾力打造,助力高校教学数字化转型
  • 产品经理如何做好需求管理
  • 国产三维CAD皇冠CAD(CrownCAD)建模教程:插接箱
  • 安科瑞AcrelEMS3.0企业微电网智慧能源平台-安科瑞 蒋静
  • ZYNQ Overlay硬件库使用指南:用Python玩转FPGA加速
  • OpenCV 级联分类器目标检测
  • 第五部分:第五节 - Express 路由与中间件进阶:厨房的分工与异常处理
  • Linux 系统切换国内镜像源教程
  • 2025年上软考 考试时间+准考证打印全攻略
  • Redis(三) - 使用Java操作Redis详解
  • 昇腾NPU环境搭建
  • 深度学习推理引擎---TensorRT
  • 外企聊营商|上海仲裁:化解跨国企业纠纷的“上海路径”
  • 101岁陕西省军区原司令员冀廷璧逝世,曾参加百团大战
  • 著名心血管病学专家李国庆教授逝世,享年63岁
  • 人民日报整版聚焦:外贸产品拓内销提速增量,多地加快推动内外贸一体化
  • 中国—美国经贸合作对接交流会在华盛顿成功举行
  • 30平米的无障碍酒吧里,我们将偏见折叠又摊开