🚀 Redisson 分布式锁源码解析:从加锁到解锁的完整流程

适用版本:Redisson 3.23.5+

核心组件:RedissonLock(可重入锁实现)

涉及模块:Lua 脚本、Redis 发布订阅、内部续期机制(看门狗)


📌 一、Redisson 分布式锁实现简介

Redisson 基于 Redis 实现了多种分布式锁的高级封装,其中 RedissonLock 实现了类似 ReentrantLock 的可重入锁特性。它利用 Redis 的原子操作和发布订阅机制,配合 Lua 脚本、异步任务和线程 ID 维护锁状态。


📍 二、加锁流程源码解析

🔧 外部入口

java 复制代码
RLock lock = redissonClient.getLock("myLock");
lock.tryLock(10, 30, TimeUnit.SECONDS); // 等待最多10秒,成功后30秒自动释放

会进入以下同步方法:

java 复制代码
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException

🧩 Step 1:尝试加锁 tryAcquire(...)

java 复制代码
Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);

内部调用的是 Lua 脚本,核心逻辑:

lua 复制代码
if lock not exists:
    hset(lockName, threadId, 1)
    pexpire(lockName, leaseTime)
else if same thread:
    hincrby(lockName, threadId, 1)
    pexpire(lockName, leaseTime)
else:
    return TTL (锁被占用)

返回值:

  • null:加锁成功
  • ttl:加锁失败,返回锁剩余时间

🧩 Step 2:加锁失败 → 订阅发布频道

java 复制代码
CompletableFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);

订阅的是该锁的发布频道:

java 复制代码
String getChannelName() = prefix + ":{" + lockName + "}:channel"

底层通过 PubSubConnectionEntry 注册订阅,封装为 RedissonLockEntry,包含 CountDownLatch


🧩 Step 3:循环等待并重试加锁

java 复制代码
while (true) {
    Long ttl = tryAcquire(...)
    if (ttl == null) return true;

    latch.tryAcquire(ttl or remainTime)
}

每次被唤醒后再次尝试加锁,直到:

  • 成功获取锁
  • 超时返回 false
  • 异常终止

📍 三、解锁流程源码解析

🔧 unlock() → unlockInnerAsync(...)

java 复制代码
public void unlock() {
    get(unlockInnerAsync(threadId));
}

核心解锁逻辑为:

java 复制代码
protected RFuture<Boolean> unlockInnerAsync(...) {
    return evalWrite(..., Lua脚本, keys, args);
}

🧩 Lua 脚本执行释放逻辑

lua 复制代码
if hexists(lock, threadId) == 0:
    return nil

counter = hincrby(lock, threadId, -1)
if counter > 0:
    pexpire(lock, leaseTime)
else:
    del(lock)
    publish(channel, unlockMessage) -- 唤醒其他订阅者

✅ 只有当线程重入次数减到 0 时,才真正释放锁并发布消息


🧩 发布消息通知其他等待线程

java 复制代码
redis.call(ARGV[4], KEYS[2], ARGV[1])

即:

redis 复制代码
PUBLISH <channel> <unlockMessage>

LockPubSub 监听该频道,唤醒之前 subscribe() 创建的 RedissonLockEntry.getLatch()


📍 四、看门狗机制(Watchdog)

若调用如下代码:

java 复制代码
lock.lock(); // 不指定 leaseTime

Redisson 会自动启用看门狗机制,每隔 10s 续约一次(默认租期 30s):

java 复制代码
scheduleExpirationRenewal(threadId)

核心续约命令:

java 复制代码
pexpire(lockKey, 30000)

续期任务在:

  • 锁未释放前持续运行
  • 线程释放锁后自动取消

✅ 五、源码流程图(文字版)

scss 复制代码
┌────────────────────────┐
│       tryLock()        │
└────────────┬───────────┘
             ↓
┌────────────────────────┐
│   tryAcquire (Lua脚本) │
│  - 加锁成功 → 返回null │
│  - 失败 → 返回TTL时间  │
└────────────┬───────────┘
             ↓
┌────────────────────────────┐
│   subscribe + latch等待    │
│   订阅释放频道,等待通知   │
└────────────┬───────────────┘
             ↓
┌────────────────────────┐
│   收到消息重新tryAcquire │
│   成功 → true 失败→重试  │
└────────────┬───────────┘
             ↓
┌────────────────────────────┐
│           unlock()         │
│   - hincrby重入次数减1     │
│   - 发布unlock通知         │
└────────────────────────────┘

📍 六、小结与建议

特性 描述
加锁脚本 Lua 保证原子性,兼容可重入逻辑
解锁通知 Redis 发布订阅频道通知其他线程唤醒尝试加锁
看门狗续期 解决业务执行过长导致锁过期问题(自动延期)
支持公平锁 Redisson 另有 RedissonFairLock 实现公平队列(推荐高并发下使用)

📚 七、推荐阅读源码类

  • RedissonLock:核心加锁解锁逻辑
  • LockPubSub:发布订阅机制
  • CommandAsyncExecutor:命令异步封装
  • RenewalTask:看门狗定时器逻辑
  • tryAcquireAsync0():真正的加锁内部实现
  • evalWrite(...):执行 Lua 脚本的统一方法
相关推荐
oak隔壁找我3 小时前
MySQL中 SHOW FULL PROCESSLIST` 输出中 `State` 列的所有可能值
后端
上进小菜猪4 小时前
基于 YOLOv8 的面向文档智能处理的表格区域检测系统 [目标检测完整源码]
后端
oak隔壁找我4 小时前
JVM常用调优参数
java·后端
IT_陈寒7 小时前
React状态管理终极对决:Redux vs Context API谁更胜一筹?
前端·人工智能·后端
晨星shine8 小时前
GC、Dispose、Unmanaged Resource 和 Managed Resource
后端·c#
蝎子莱莱爱打怪8 小时前
OpenClaw 从零配置指南:接入飞书 + 常用命令 + 原理图解
java·后端·ai编程
倚栏听风雨9 小时前
【ES避坑指南】明明存的是 "CodingAddress",为什么 term 查询死活查不到?彻底搞懂 text 和 keyword
后端
程序员爱钓鱼9 小时前
Go 操作 Windows COM 自动化实战:深入解析 go-ole
后端·go·排序算法
回家路上绕了弯9 小时前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
子玖9 小时前
实现微信扫码注册登录-基于参数二维码
后端·微信·go