🚀 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 小时前
java面向对象四大核心特征之抽象---超详细(保姆级)
java·后端
Aurora_NeAr2 小时前
对比Java学习Go——程序结构与变量
后端
AntBlack2 小时前
每周学点 AI:ComfyUI + Modal 的一键部署脚本
人工智能·后端·aigc
5大大大大雄3 小时前
docker容器日志处理
后端
我是哪吒3 小时前
分布式微服务系统架构第170集:Kafka消费者并发-多节点消费-可扩展性
后端·面试·github
Badman4 小时前
分布式系统下的数据一致性-Redis分布式锁
redis·分布式·后端
Java水解4 小时前
盘点那些自带高级算法的SQL
后端
一只叫煤球的猫5 小时前
2025年基于Java21的的秒杀系统要怎么设计?来点干货
后端·面试·性能优化
方圆想当图灵5 小时前
《生产微服务》评估清单 CheckList
后端·微服务
服务端技术栈5 小时前
历时 1 个多月,我的第一个微信小程序「图片转 Excel」终于上线了!
前端·后端·微信小程序