连载中 4/20

Redisson:可重入锁的完整实现

2026-06-26 · 2561 阅读 · 0 评论 · 0 赞

不会重入的锁,会自杀

上一篇攒出的三件套(SET NX PX 抢锁、Lua 释放、Lua 续期)看着完整,跑进真实业务立刻暴露一个缺口:锁不可重入。场景很日常——扣款入口方法上抢了锁 lock:card:8801,方法内部要调风控校验,风控方法上又标了同一把锁。线程第一层加锁成功,进到第二层再抢锁——锁就在自己手里,但 SET NX 一看 key 存在,拒绝——线程等自己释放锁,自己永远等不到,自己把自己锁死了。synchronized 和 ReentrantLock 都是可重入的,业务代码顺着 JVM 的习惯写,分布式锁不重入就是给自己埋雷。

Redisson 三行代码

Java 生态里没理由自己造这个轮子,Redisson 把上一篇的全部问题连同这篇的重入一起解决了:

RLock lock = redissonClient.getLock("lock:card:8801");
lock.lock();                 // 抢锁,不传 leaseTime,看门狗自动接管
try {
    // 临界区业务
} finally {
    lock.unlock();            // 必须放在 finally,异常也要释放
}

三行代码背后藏着一个关键纪律:unlock 必须放 finally——业务抛异常不释放锁,等于把永生锁的坑亲手挖回来。

Hash 结构:重入计数的载体

Redisson 为什么用 Hash 而不是 String 存锁?看它加锁的 Lua 逻辑(简化版):

-- KEYS[1]=锁名  ARGV[1]=客户端ID:线程ID  ARGV[2]=TTL毫秒
if redis.call("EXISTS", KEYS[1]) == 0 then
    redis.call("HSET", KEYS[1], ARGV[1], 1)        -- 空锁:首次加锁,计数=1
    redis.call("PEXPIRE", KEYS[1], ARGV[2])
    return nil
end
if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
    redis.call("HINCRBY", KEYS[1], ARGV[1], 1)     -- 我的锁:重入,计数+1
    redis.call("PEXPIRE", KEYS[1], ARGV[2])
    return nil
end
return redis.call("PTTL", KEYS[1])                  -- 别人的锁:返回剩余过期时间

锁名做 key,持有人标识(客户端 ID 加线程 ID)做 field,重入次数做 value:lock:card:8801 → {"a1b2:thread-17": 2},意思是线程 17 进了两次临界区。解锁是反着来(也是 Lua):重入计数减一,减到零才真正 DEL——内层方法退锁只减计数,外层方法退锁才放钥匙,嵌套的进和出一一对应。顺便,第三个分支返回 PTTL(剩余过期时间)是个精巧设计:抢锁失败的线程知道「还有 4 秒过期」,据此安排重试节奏,不用傻傻轮询。

看门狗的开关藏在一个参数里

Redisson 默认 TTL 30 秒,看门狗每 10 秒(TTL 的三分之一)续一次,上一篇的机制原样内置。但有个容易翻车的细节:看门狗只在「不指定 leaseTime」时才启动。lock.lock() 走看门狗;lock.lock(10, TimeUnit.SECONDS) 指定了租期,Redisson 认为你「自己管理时长」,10 秒后锁准时过期,看门狗不续。很多事故就出在这一行——团队某人图省事传了个 leaseTime,业务一抖锁先没了,互斥破碎查半天。给个决策口诀:临界区时长不可控,用无参 lock 交给看门狗;时长确定且短(比如缓存重建),才用带 leaseTime 的定租

用法TTL 行为适用场景
lock.lock()30 秒,看门狗每 10 秒续期业务时长不可控的默认选择
lock.lock(10, SECONDS)10 秒,无续期,到期自动释放时长确定,宁可锁过期也别卡死
lock.tryLock(2, 30, SECONDS)最多等 2 秒抢锁,租期 30 秒抢不到就放弃的限时任务

Redisson 家族里还有公平锁(按请求顺序排队)、读写锁(读共享写互斥)、联锁(多把锁同时加)等变体,思想同源不展开。值得记一笔的是它对 RedLock 的实现(RRedLock)——这是 Redis 官方作者为「锁在主从切换时丢失」开的药方,而这份药方恰恰是分布式锁历史上最著名的一场论战的主角。下一篇,把 RedLock 的来龙去脉和那场隔空交手讲透。

503

10 年全栈工程师 · 503咖啡馆主理人

#分布式锁#Redisson#可重入锁#看门狗#Hash结构

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞