🚀 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 脚本的统一方法
相关推荐
舒一笑1 小时前
PandaCoder:致敬MyBatis Log Plugin,但我们做得更极致!
后端·程序员·intellij idea
简单点了3 小时前
go前后端项目的启动 、打包和部署
开发语言·后端·golang
雨夜之寂4 小时前
mcp java实战 第一章-第一节-MCP协议简介.md
java·后端
摇滚侠4 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 核心语法 笔记39
spring boot·笔记·后端·thymeleaf
Code blocks6 小时前
GB28181视频服务wvp搭建(二)
后端·音视频
码事漫谈6 小时前
C++与C#布尔类型深度解析:从语言设计到跨平台互操作
后端
程序新视界6 小时前
在MySQL中,一条SQL语句的执行全流程是怎样的?
数据库·后端·mysql
lang201509286 小时前
打造专属Spring Boot Starter
java·spring boot·后端
码事漫谈7 小时前
解决Python调用C++ DLL失败的问题:extern "C"的关键作用
后端
码事漫谈7 小时前
从「能用」到「可靠」:深入探讨C++异常安全
后端