news 2026/8/9 6:17:14

Redisson MultiLock 原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redisson MultiLock 原理

1. 什么是 MultiLock

Redisson 的 MultiLock(多重锁)是一种分布式锁机制,它允许将多个独立的RLock实例组合成一个逻辑上的锁。当用户需要对多个资源同时加锁时,MultiLock 可以保证这些资源要么全部被锁定,要么全部不被锁定,从而避免部分锁定导致的死锁或数据不一致问题。

2. 核心机制

MultiLock 本身并不是一把新的 Redis 锁,而是一个对多个子锁(RLock)的统一管理器。其加锁过程如下:

  • 顺序加锁:按传入顺序依次尝试获取每一个子锁。
  • 全量成功:只有所有子锁都获取成功,MultiLock 才认为加锁成功。
  • 失败回滚:如果在获取某个子锁时失败,会立即释放已经获取的所有子锁,恢复到加锁前的状态。
  • 异步非阻塞:底层使用 Redisson 的异步非阻塞模型,通过 Lua 脚本在 Redis 服务端保证原子性操作。

3. MultiLock 的特性

  • 原子性操作:通过 Lua 脚本确保加锁和解锁的原子性,避免部分成功或失败。
  • 可重入性:支持同一个线程多次加锁,每次解锁只减少持有计数,计数归零时才真正释放锁。
  • 超时机制:支持设置锁的自动释放时间,防止死锁。
  • 公平锁与非公平锁:根据底层RLock的配置,可以支持公平或非公平锁模式。

4. 代码示例

以下是一个使用 MultiLock 的典型 Java 示例:

RLock lock1 = redisson.getLock("lock1"); RLock lock2 = redisson.getLock("lock2"); RLock lock3 = redisson.getLock("lock3"); // 创建 MultiLock RLock multiLock = redisson.getMultiLock(lock1, lock2, lock3); // 尝试加锁,设置等待时间和锁自动释放时间 boolean isLocked = multiLock.tryLock(10, 30, TimeUnit.SECONDS); if (isLocked) { try { // 执行业务逻辑 } finally { multiLock.unlock(); } }

5. 底层 Lua 脚本

加锁和解锁的核心逻辑由 Lua 脚本在 Redis 服务端执行。以下是加锁的简化逻辑:

-- 参数:KEYS[1..n] 为锁的 key,ARGV[1] 为超时时间,ARGV[2] 为线程标识 local failedLocks = {} for i, key in ipairs(KEYS) do if redis.call('exists', key) == 0 then redis.call('hset', key, ARGV[2], 1) redis.call('pexpire', key, ARGV[1]) else table.insert(failedLocks, key) end end if #failedLocks > 0 then -- 释放已获取的锁,进行回滚 for i, key in ipairs(KEYS) do if not table.contains(failedLocks, key) then redis.call('del', key) end end return 0 end return 1

进一步刨析里面的trylock()源码:代码来源于https://cyborg2077.github.io/2022/10/22/RedisPractice/#Redisson%E9%94%81%E7%9A%84MutiLock%E5%8E%9F%E7%90%86

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { long newLeaseTime = -1L; //如果传入了释放时间 if (leaseTime != -1L) { //再判断一下是否有等待时间 if (waitTime == -1L) { //如果没传等待时间,不重试,则只获得一次 newLeaseTime = unit.toMillis(leaseTime); } else { //想要重试,耗时较久,万一释放时间小于等待时间,则会有问题,所以这里将等待时间乘以二 newLeaseTime = unit.toMillis(waitTime) * 2L; } } //获取当前时间 long time = System.currentTimeMillis(); //剩余等待时间 long remainTime = -1L; if (waitTime != -1L) { remainTime = unit.toMillis(waitTime); } //锁等待时间,与剩余等待时间一样 long lockWaitTime = this.calcLockWaitTime(remainTime); //锁失败的限制,源码返回是的0 int failedLocksLimit = this.failedLocksLimit(); //已经获取成功的锁 List<RLock> acquiredLocks = new ArrayList(this.locks.size()); //迭代器,用于遍历 ListIterator<RLock> iterator = this.locks.listIterator(); while(iterator.hasNext()) { RLock lock = (RLock)iterator.next(); boolean lockAcquired; try { //没有等待时间和释放时间,调用空参的tryLock if (waitTime == -1L && leaseTime == -1L) { lockAcquired = lock.tryLock(); } else { //否则调用带参的tryLock long awaitTime = Math.min(lockWaitTime, remainTime); lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS); } } catch (RedisResponseTimeoutException var21) { this.unlockInner(Arrays.asList(lock)); lockAcquired = false; } catch (Exception var22) { lockAcquired = false; } //判断获取锁是否成功 if (lockAcquired) { //成功则将锁放入成功锁的集合 acquiredLocks.add(lock); } else { //如果获取锁失败 //判断当前锁的数量,减去成功获取锁的数量,如果为0,则所有锁都成功获取,跳出循环 if (this.locks.size() - acquiredLocks.size() == this.failedLocksLimit()) { break; } //否则将拿到的锁都释放掉 if (failedLocksLimit == 0) { this.unlockInner(acquiredLocks); //如果等待时间为-1,则不想重试,直接返回false if (waitTime == -1L) { return false; } failedLocksLimit = this.failedLocksLimit(); //将已经拿到的锁都清空 acquiredLocks.clear(); //将迭代器往前迭代,相当于重置指针,放到第一个然后重试获取锁 while(iterator.hasPrevious()) { iterator.previous(); } } else { --failedLocksLimit; } } //如果剩余时间不为-1,很充足 if (remainTime != -1L) { //计算现在剩余时间 remainTime -= System.currentTimeMillis() - time; time = System.currentTimeMillis(); //如果剩余时间为负数,则获取锁超时了 if (remainTime <= 0L) { //将之前已经获取到的锁释放掉,并返回false this.unlockInner(acquiredLocks); //联锁成功的条件是:每一把锁都必须成功获取,一把锁失败,则都失败 return false; } } } //如果设置了锁的有效期,因为没把锁的获取时间都不一样,所以剩余过期时间不一样,这里就给时间重新赋值 if (leaseTime != -1L) { List<RFuture<Boolean>> futures = new ArrayList(acquiredLocks.size()); //迭代器用于遍历已经获取成功的锁 Iterator var24 = acquiredLocks.iterator(); while(var24.hasNext()) { RLock rLock = (RLock)var24.next(); //设置每一把锁的有效期 RFuture<Boolean> future = ((RedissonLock)rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS); futures.add(future); } var24 = futures.iterator(); while(var24.hasNext()) { RFuture<Boolean> rFuture = (RFuture)var24.next(); rFuture.syncUninterruptibly(); } } //但如果没设置有效期,则会触发WatchDog机制,自动帮我们设置有效期,所以大多数情况下,我们不需要自己设置有效期 return true; }

6. 适用场景

  • 多资源事务:需要同时锁定多个资源以保证事务的原子性。
  • 跨服务调用:在分布式系统中,多个服务需要协同完成某个操作时。
  • 避免死锁:通过统一的加锁和解锁机制,减少死锁风险。

7. 总结

Redisson MultiLock 通过组合多个独立的RLock实例,配合 Lua 脚本实现的原子性加锁回滚机制,为分布式系统提供了可靠的多资源锁定能力。理解其原理有助于在需要同时锁定多个资源时,设计出更健壮、更安全的并发控制方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/9 6:16:23

Claude Code 安装配置全攻略:从 Node.js 环境到 Coding Plan 集成

1. 项目概述&#xff1a;从零到一&#xff0c;搭建你的智能编程伙伴 最近在开发者圈子里&#xff0c;Claude Code 的热度持续攀升。这不仅仅是一个代码补全工具&#xff0c;更像是一个能理解你意图、帮你重构、甚至能写单元测试的“结对编程”伙伴。但很多朋友在第一步——安装…

作者头像 李华
网站建设 2026/8/9 6:16:01

大一新生必读:大学四年高效成长指南

1. 写给大一新生的成长指南刚踏入大学校园时&#xff0c;那种既兴奋又迷茫的感觉我至今记忆犹新。作为过来人&#xff0c;我想分享一些如果时光能倒流&#xff0c;我一定会告诉大一自己的重要建议。这些经验不仅来自我个人&#xff0c;也汇集了众多学长学姐的血泪教训。大学四年…

作者头像 李华
网站建设 2026/8/9 6:14:15

Unity时间戳转换性能优化:从DateTime到高效实现的深度解析

1. 项目概述&#xff1a;为什么Unity开发者必须关注时间戳转换在Unity游戏开发里&#xff0c;处理时间数据是家常便饭。从记录玩家登录时间、计算技能冷却、到同步服务器状态、保存游戏存档&#xff0c;几乎每个功能模块都离不开它。你可能随手就用DateTime.Now或者Time.time&a…

作者头像 李华
网站建设 2026/8/9 6:13:40

轻量级HTTP压测工具Weighttp:原理、实战与性能分析指南

1. 项目概述&#xff1a;为什么我们需要一个“轻量级”的压测工具&#xff1f;在开发和运维Web服务的日常工作中&#xff0c;性能基准测试&#xff08;Benchmarking&#xff09;是一个绕不开的环节。无论是评估新上线的Nginx配置调优效果&#xff0c;还是对比不同后端框架&…

作者头像 李华
网站建设 2026/8/9 6:12:59

Unity开发中C#静态成员深度解析:从内存模型到实战避坑指南

1. 项目概述&#xff1a;为什么Unity开发者必须吃透C#的static如果你正在用Unity做游戏&#xff0c;或者刚入门C#&#xff0c;那么“静态成员”这个概念&#xff0c;你大概率是既熟悉又陌生。熟悉是因为你肯定在代码里见过static这个关键字&#xff0c;比如Unity自带的Debug.Lo…

作者头像 李华
网站建设 2026/8/9 6:12:49

YOLO乒乓球比赛落点与旋转类型目标检测数据集

YOLO乒乓球比赛落点与旋转类型目标检测数据集YOLO26 小数据集目标检测训练全流程&#x1f4ca; 数据集基本信息 目标类别&#xff1a; [‘landing-point’, ‘negative-slope’, ‘other’, ‘positive-slope’, ‘zero-slope’]中文类别&#xff1a;[‘落点’, ‘下旋球’, ‘…

作者头像 李华