Redisson 四大核心机制实现原理详解
一、可重入锁(Reentrant Lock)
可重入锁是什么?
通俗定义
可重入锁类似于一把“智能锁”,它能识别当前的锁持有者是否是当前线程:
- 如果是,则允许线程重复获取锁(重入),并记录重入次数。
- 如果不是,则其他线程必须等待锁释放后才能获取。
典型场景
当一个线程调用了一个被锁保护的方法A,而方法A内部又调用了另一个被同一锁保护的方法B时,如果锁不可重入,线程会在调用方法B时被自己阻塞(死锁)。可重入锁允许这种嵌套调用。
public class Demo {private final Lock lock = new SomeLock(); // 假设这是一个锁public void methodA() {lock.lock();try {methodB(); // 调用另一个需要加锁的方法} finally {lock.unlock();}}public void methodB() {lock.lock();try {// 业务逻辑} finally {lock.unlock();}} }
- 如果锁不可重入 线程进入
methodA
获取锁后,调用methodB
时再次尝试加锁,会因为锁已被自己持有而永久阻塞(死锁)。- 如果锁可重入 线程在
methodB
中能成功获取锁,计数器从1
增加到2
,释放时计数器递减,最终正常释放。
实现原理:通过 Redis 的 Hash 结构实现线程级锁的可重入性。
-
数据结构:
- Key:锁名称(如
lock:order:1001
)。 - Field:客户端唯一标识(
UUID + 线程ID
),如b983c153-7091-42d8-823a-cb332d52d2a6:1
。 - Value:锁的 重入次数(初始为 1,重入时递增)。
- Key:锁名称(如
-
加锁逻辑:
- 首次加锁:执行 Lua 脚本,若 Key 不存在,创建 Hash 并设置重入次数为 1。
-- KEYS[1]=锁名, ARGV[1]=锁超时时间, ARGV[2]=线程唯一ID if (redis.call('exists', KEYS[1]) == 0) then -- 如果锁不存在redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 创建Hash,记录线程重入次数redis.call('pexpire', KEYS[1], ARGV[1]); -- 设置锁超时时间return nil; -- 返回成功 end;
- 重入加锁:若 Field 匹配当前线程,重入次数 +1。
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 如果锁已被当前线程持有redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 增加重入次数redis.call('pexpire', KEYS[1], ARGV[1]); -- 刷新锁超时时间return nil; -- 返回成功 end;
- 首次加锁:执行 Lua 脚本,若 Key 不存在,创建 Hash 并设置重入次数为 1。
-
释放锁:减少重入次数,归零时删除 Hash。
-- KEYS[1]: 锁名称(如 my_lock) -- KEYS[2]: 发布订阅的频道名 -- ARGV[1]: 解锁消息标识(如 0) -- ARGV[2]: 锁的过期时间(毫秒) -- ARGV[3]: 客户端唯一标识(UUID + 线程ID)-- 检查锁是否存在且属于当前线程 if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) thenreturn nil; -- 锁不存在或不属于当前线程,直接返回 end;-- 减少重入计数器(原子操作) local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);if (counter > 0) then-- 仍有重入未释放完,更新锁过期时间redis.call('pexpire', KEYS[1], ARGV[2]);return 0; -- 返回0表示未完全释放 else-- 计数器归零,删除锁并发布释放通知redis.call('del', KEYS[1]);redis.call('publish', KEYS[2], ARGV[1]);return 1; -- 返回1表示锁已完全释放 end;
二、锁重试机制(Retry Mechanism)
重试机制的触发条件
当调用
tryLock(long waitTime, long leaseTime, TimeUnit unit)
方法时,若waitTime > 0
,Redisson 会启用重试机制。例如:java// 10秒内不断重试获取锁,获取成功后持有锁60秒 lock.tryLock(10, 60, TimeUnit.SECONDS);
若首次获取锁失败,进入重试流程。
实现原理: 事件驱动优先,主动轮询兜底
-
首次尝试获取锁
- 原子性操作:通过 Lua 脚本尝试获取锁(检查锁是否存在或是否属于当前线程)。
- 失败返回值:若锁被其他线程持有,返回锁的剩余存活时间(
ttl
)。
-
订阅锁释放事件
- 创建监听频道:订阅 Redis 频道
redisson_lock__channel:{lockName}
。 - 事件驱动优化:避免频繁轮询,仅当锁释放时触发重试,减少无效请求。
// 伪代码:订阅锁释放事件 RFuture<RedissonLockEntry> future = subscribe(lockName); RedissonLockEntry entry = get(future);
- 创建监听频道:订阅 Redis 频道
-
循环重试(主动轮询 + 事件触发)
- 计算剩余等待时间:基于
waitTime
和已消耗时间,动态调整剩余等待窗口。 - 双重检测逻辑:
- 主动轮询:定期(默认间隔 100ms ~ 300ms)执行 Lua 脚本尝试获取锁。
- 事件触发:收到锁释放通知后立即尝试获取锁。
- 退避策略:每次重试失败后,采用随机递增的等待时间(避免多个客户端同时竞争导致雪崩)。
关键代码逻辑(简化):
- 计算剩余等待时间:基于
long remainingTime = waitTime; // 剩余等待时间
long startTime = System.currentTimeMillis();while (remainingTime > 0) {// 1. 尝试获取锁Long ttl = tryAcquire(leaseTime, unit); // 调用Lua脚本if (ttl == null) {return true; // 获取成功}// 2. 计算剩余时间long elapsed = System.currentTimeMillis() - startTime;remainingTime -= elapsed;if (remainingTime <= 0) {break; // 超时退出}// 3. 等待锁释放事件或超时entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); // 基于信号量等待// 4. 更新剩余时间remainingTime -= (System.currentTimeMillis() - startTime - elapsed);
}
return false; // 超时未获取
- 超时终止
- 时间窗口耗尽:若总耗时超过
waitTime
,终止重试并返回失败。 - 资源清理:取消 Redis 订阅,释放连接。
- 时间窗口耗尽:若总耗时超过
三、WatchDog 看门狗(锁续期机制)
防止业务执行时间超过锁的过期时间,导致锁提前释放。
启用看门狗需满足以下条件之一:
- 未显式指定锁的租约时间(leaseTime): 例如调用
lock.tryLock()
或lock.lock()
时不传leaseTime
参数。- 显式设置租约时间为
-1
: 例如lock.tryLock(10, -1, TimeUnit.SECONDS)
。注意:若指定了固定的
leaseTime
(如lock.tryLock(10, 30, TimeUnit.SECONDS)
),看门狗不会启动,锁会在 30 秒后自动释放。
实现原理:后台线程自动续期锁,防止业务未完成时锁过期。
-
触发条件:未指定锁超时时间(如
lock.lock()
)。 -
续期逻辑:
-
定时任务:默认每 10 秒(
lockWatchdogTimeout / 3
)续期一次。 -
续期命令:重置锁的过期时间为 30 秒(默认值)。
-- KEYS[1]: 锁名称 -- ARGV[1]: 过期时间(默认30秒) -- ARGV[2]: 客户端唯一标识 if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) thenredis.call('pexpire', KEYS[1], ARGV[1]);return 1; end; return 0;
-
-
终止条件:
- 锁被释放(
unlock()
调用)。 - 客户端断开连接或线程中断。
- 锁被释放(
四、主从一致性(MultiLock/RedLock)
Redis 主从复制是异步的,若主节点宕机且锁未同步到从节点,可能导致多个客户端同时持有锁。
实现原理:基于多数派原则,向多个独立节点加锁。
-
MultiLock 流程:
- 加锁:向所有节点发送加锁请求,需 半数以上成功(如 3 节点至少 2 个成功)。
- 容错:允许最多
⌊(N-1)/2⌋
个节点故障(如 5 节点允许 2 个故障)。 - 解锁:无论加锁是否成功,向所有节点发送解锁命令。
-
RedLock 算法增强:
- 时钟同步:要求节点使用 NTP 同步时间,锁有效期需包含时钟漂移。
- 加锁验证:计算加锁耗时,确保有效时间未耗尽。
-
配置示例:
RLock lock1 = redissonClient1.getLock("lock"); RLock lock2 = redissonClient2.getLock("lock"); RLock multiLock = new RedissonMultiLock(lock1, lock2); multiLock.lock(); try {// 业务逻辑 } finally {multiLock.unlock(); }
五、总结
机制 | 实现原理 |
---|---|
可重入锁 | 使用 Redis Hash 结构存储锁名、线程唯一标识(UUID+线程ID)和重入次数。同一线程多次获取锁时重入次数递增,释放时递减,归零后删除锁。 |
锁重试 | 通过 Pub/Sub 订阅锁释放事件 避免轮询;失败后按退避策略(默认 1.5 秒)重试,直到超时或成功。 |
WatchDog | 后台线程每 10 秒(默认)检查锁持有状态,若锁存在则续期(重置过期时间至 30 秒)。未指定锁超时时间时自动启用。 |
主从一致性 | 使用 MultiLock/RedLock:向多个独立节点加锁,需半数以上成功;解锁时向所有节点发送命令,解决主从异步复制导致的锁失效。 |
相关文章:
Redisson 四大核心机制实现原理详解
一、可重入锁(Reentrant Lock) 可重入锁是什么? 通俗定义 可重入锁类似于一把“智能锁”,它能识别当前的锁持有者是否是当前线程: 如果是,则允许线程重复获取锁(重入),并…...

云鼎入鼎系统:一站式电商管理解决方案
个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119qq.com] 📱个人微信&a…...

Leetcode134加油站
题目链接 134 题意图解: 题目给了n个节点,这些节点呈现环状,每次到一个低点要消耗cost[i]的油量。 从中我们可以得出一个结论:看一个点能不能到下一个点,就要用当前的油量减去消耗的量,那么gas[i] - cost…...

关于Android Studio for Platform的使用记录
文章目录 简单介绍如何使用配置导入aosp工程配置文件asfp-config.json 简单介绍 Android Studio for Platform是google最新开发,用来阅读aosp源码的工具 详细的资料介绍: https://developer.android.google.cn/studio/platform 将工具下载下来直接点击…...
Linux的内存泄漏问题及排查方法
内存泄漏是指在计算机程序中,已不再被使用的内存未被正确释放,导致内存占用随时间累积,进而引发系统内存不足、性能下降甚至崩溃的问题。在Linux系统中,开发者和运维人员可通过以下方法排查和解决内存泄漏问题: 1. 使…...

uniapp 微信小程序 获取openId
嗨,我是小路。今天主要和大家分享的主题是“uniapp 微信小程序 获取openId”。 一、主要属性 1.uni.login 二、实例代码 1、前端代码 uni.login({provider: weixin,success: (res) > {uni.showLoading({title: 登录中...,mask: true})let code res.…...

隧道结构安全在线监测系统解决方案
一、方案背景 隧道是地下隐蔽工程,会受到潜在、无法预知的地质因素影响。随着我国公路交通建设的发展,隧道占新建公路里程的比例越来越大。隧道属于线状工程,有的规模较大,可长达几公里或数十公里,往往穿越许多不同环境…...

Docker 运维管理
Docker 运维管理 一、Swarm集群管理1.1 Swarm的核心概念1.1.1 集群1.1.2 节点1.1.3 服务和任务1.1.4 负载均衡 1.2 Swarm安装准备工作创建集群添加工作节点到集群发布服务到集群扩展一个或多个服务从集群中删除服务ssh免密登录 二、Docker Compose与 Swarm 一起使用 Compose 三…...
【Redis】快速列表结构
目录 1、背景2、压缩列表【1】底层结构【2】特性【3】优缺点 1、背景 redis的quicklist(快速列表)是一个双向链表,其中每个节点都是一个ziplist(压缩列表)。这中结构结合了双向链表和压缩列表的优点,在内存…...
阿里巴巴 1688 数据接口开发指南:构建自动化商品详情采集系统
在电商行业数据驱动决策的趋势下,高效获取商品详情数据成为企业洞察市场、优化运营的关键。通过阿里巴巴 1688 数据接口构建自动化商品详情采集系统,能够快速、精准地采集海量商品信息。本文将从开发准备、接口分析、代码实现等方面,详细介绍…...

[SpringBoot]Spring MVC(2.0)
紧接上文,这篇我们继续讲剩下的HTTp请求 传递JSON数据 简单来说:JSON就是⼀种数据格式,有⾃⼰的格式和语法,使⽤⽂本表⽰⼀个对象或数组的信息,因此JSON本质是字符串. 主要负责在不同的语⾔中数据传递和交换 JSON的语法 1. 数据在 键值对(Key/Value) …...

Golang的网络安全策略实践
Golang的网络安全策略实践 一、理解网络安全的重要性 当今的网络环境中,安全问题日益突出,各种类型的攻击如雨后春笋般涌现,给个人和组织的信息资产造成了严重威胁。因此,制定和实施有效的网络安全策略至关重要。 二、Golang在网络…...

STM32外设AD-轮询法读取模板
STM32外设AD-轮询法读取模板 一,什么是轮询?1,轮询法的直观理解2,轮询法缺点 二,CubeMX配置三,模板移植1,adc_app.c文件2,变量声明1,adc_app.c中2,mydefine.h…...
C++编程this指针练习
这段代码是用 C 编写的,定义了一个 Car 类(类是 C 中用于创建对象的蓝图),并通过 main() 函数创建和使用了该类的对象。下面是对整个程序的逐行解释,并在关键部分加上注释说明。 ✅ 代码整体功能: 定义一个…...

iOS音视频解封装分析
首先是进行解封装的简单的配置 /// 解封装配置 class KFDemuxerConfig {// 媒体资源var asset: AVAsset?// 解封装类型,指定是音频、视频或两者都需要var demuxerType: KFMediaType .avinit() {} }然后是实现解封装控制器 import Foundation import CoreMedia i…...

突破智能驾舱边界,Imagination如何构建高安全GPU+AI融合计算架构
日前,“第十二届汽车电子创新大会暨汽车芯片产业生态发展论坛(AEIF 2025)”在上海顺利举办。大会围绕汽车前沿性、关键性和颠覆性技术突破,邀请行业众多专家学者,分享与探讨了汽车电子产业的技术热点与发展趋势。在5月…...

DeepSeek 如何实现 128K 上下文窗口?
DeepSeek 如何实现 128K 上下文窗口?长文本处理技术揭秘 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 DeepSeek 如何实现 128K 上下文窗口?长文本处理技术揭秘摘要引言技术架构解析1. 动态…...
云计算简介:从“水电”到“数字引擎”的技术革命
云计算简介:从“水电”到“数字引擎”的技术革命 在当今数字化浪潮中,云计算早已从一个技术概念演变为支撑现代社会运转的核心基础设施。无论是你手机里的天气预报、电商购物的推荐系统,还是企业内部的ERP系统,背后都离不开云计算…...
计算圆周率 (python)
使用模特卡罗方法(模拟法),模拟撒点100000次,计算圆周率π 输入格式: 一个整数,表示随机数种子 输出格式: 计算的π值,结果小数点后保留5位数字 输入样例: 在这里给出一组输入。例如: 10…...

Python 实现图片浏览和选择工具
实现将截图预览,并按照顺序加入一个pdf文件中,实现照片管理尤其对于喜欢看教程截图做笔记的网友们。 C:\pythoncode\new\python-image-pdf-processor.py 界面展示 🧱 一、核心结构概述 主类 ImageViewer(wx.Frame) 是主窗口类,…...

Python实现的在线词典学习工具
Python实现的在线词典学习工具 源码最初来自网络,根据实际情况进行了修改。 主要功能: 单词查询 通过Bing词典在线获取单词释义(正则提取网页meta描述),支持回车键快速查询 内置网络请求重试和异常处理机制 在线网页…...
ES常识9:如何实现同义词映射(搜索)
在 Elasticsearch(ES)中实现同义词映射(如“美丽”和“漂亮”),核心是通过 同义词过滤器(Synonym Token Filter) 在分词阶段将同义词扩展或替换为统一词项,从而让搜索时输入任意一个…...

BGP综合实验(2)
一、实验需求 1、实验拓扑图 2、实验需求 使用 PreVal 策略,让 R4 经 R2 到达 192.168.10.0/24 。 使用 AS_Path 策略,让 R4 经 R3 到达 192.168.11.0/24 。 配置 MED 策略,让 R4 经 R3 到达 192.168.12.0/24 。 使用 Local Preference 策…...
java实现poi-ooxml导出Excel的功能
文章目录 1. 添加poi-ooxml依赖2. Excel导出工具类3.核心逻辑说明4.扩展建议5.HSSF、XSSF、SXSSF 的核心原则和场景建议,帮助你在不同需求下快速决策: 以下是一个基于 Apache POI 实现的简单、通用的Java导出Excel工具类,代码逻辑清晰且注释详…...

代码随想录算法训练营 Day51 图论Ⅱ岛屿问题Ⅰ
图论 题目 99. 岛屿数量 使用 DFS 实现方法 判断岛屿方法 1. 遍历图,若遍历到了陆地 grid[i][j] 1 并且陆地没有被访问,在这个陆地的基础上进行 DFS 方法,或者是 BFS 方法 2. 对陆地进行 DFS 的时候时刻注意以访问的元素添加访问标记 //…...

【占融数科-注册/登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

【CF】Day62——Codeforces Round 948 (Div. 2) CD (思维 + LCM + 枚举因数 | 思维 + 哈希)
C. Nikita and LCM 题目: 思路: 非常好的思维题,顺便复习了一下快速枚举因数和lcm的性质 我们先来看答案的上界,即全选,此时说明 lcm(a1,a2,a3,...) > a_max 其中 a_max 为 a 中最大的数,那么如果答案不…...

基于requests_html的python爬虫
前言:今天介绍一个相对性能更高的爬虫库requests_html,会不会感觉和requests有点联系?是的。为什么开始不直接介绍呢?因为我觉得requests是最基本入门的东西,并且在学习过程中也能学到很多东西。我的python老师在介绍这…...
循环神经网络:捕捉序列数据中的时间信息
目录 循环神经网络:捕捉序列数据中的时间信息 一、循环神经网络的基本概念 (一)RNN 的基本结构 (二)RNN 的工作原理 (三)RNN 的优势 (四)RNN 的局限性 二、循环神…...
第35周Zookkeeper+Dubbo 面试题精讲
面试题精讲 一、算法面试答题思路 理解思路的重要性:算法面试比基础面试更复杂,需先想清楚思路,与面试官沟通确认题目条件(如数据范围、是否包含负数/零等),这有助于理清解题思路并展示技术实力。变量命名清晰:算法中变量命名要明确含义和范围,避免使用模糊的变量名,…...