🚀 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 脚本的统一方法
相关推荐
野犬寒鸦7 分钟前
从零起步学习RabbitMQ || 第二章:RabbitMQ 深入理解概念 Producer、Consumer、Exchange、Queue 与企业实战案例
java·服务器·数据库·分布式·后端·rabbitmq
IT_陈寒7 分钟前
Vite 4.0实战:5个被低估的配置项让构建速度提升50%
前端·人工智能·后端
码界奇点15 分钟前
基于Spring Boot与MyBatis-Plus的后台管理系统设计与实现
spring boot·后端·车载系统·毕业设计·mybatis·源代码管理
sunnyday042616 分钟前
Spring Boot 应用启动成功后的事件监听与日志输出实践
java·spring boot·后端
短剑重铸之日20 分钟前
《7天学会Redis》Day 7 - Redisson 全览
java·数据库·redis·后端·缓存·redission
Qiuner21 分钟前
一文读懂 Lambda
java·spring boot·后端·架构
程序新视界26 分钟前
“提供溢出的情绪价值”是AI产品极具可能性的方向
人工智能·后端·产品
a程序小傲28 分钟前
中国邮政Java面试被问:Kafka的Log Compaction实现和删除策略
java·开发语言·后端·python·面试·职场和发展·kafka
岁岁种桃花儿28 分钟前
Spring Boot @GetMapping注解:从应用到原理深度解析
java·spring boot·后端
颜淡慕潇29 分钟前
Spring Boot 3.x 升级实战:3.0 → 3.5:为什么升、升什么、以及我们是怎么升的
java·spring boot·后端