redisson看门狗机制默认启用,当调用lock()或trylock()未指定leasetime时,启动后台守护线程每10秒执行lua脚本续期锁(默认30秒租期),校验线程id确保安全;续期失败则锁按原ttl自动过期。

守护线程中 finally 块未执行,本质上是锁释放逻辑被跳过——这不是 Redis 锁本身的缺陷,而是业务代码与线程生命周期配合失当引发的严重故障。核心问题在于:**把锁的生命周期错误地绑定在了可能提前终止的守护线程上,而没做兜底保障。**
必须避免在守护线程里直接管理锁释放
守护线程(Daemon Thread)会在 JVM 退出时被强制终止,不等待其执行完毕。一旦业务逻辑卡在耗时操作中、JVM 因 OOM 或 kill -9 被干掉,或线程被意外中断,try...finally 就彻底失效,锁永远滞留在 Redis 中。
- 不要写类似
new Thread(() -> { try { lock.lock(); doWork(); } finally { lock.unlock(); } }).setDaemon(true).start(); - 守护线程适合做日志刷盘、指标上报等“尽力而为”型任务,不适合承担锁这种强一致性责任。
用 Redisson 的看门狗机制替代手动守护
Redisson 的 RLock 默认启用 Watch Dog(看门狗),它不是靠用户启一个守护线程,而是由 Redisson 内部的独立 Netty EventLoop 线程池驱动续期。只要客户端连接存活,它就会每 10 秒(默认为锁 TTL 的 1/3)自动刷新锁的过期时间,且续期失败会触发告警而非静默失败。
- 加锁时无需指定过期时间:
lock.tryLock()即可启用自动续期(默认 TTL=30s) - 业务完成调用
unlock()后,看门狗自动停止,不会残留后台任务 - 即使业务线程卡住、OOM 或被中断,只要 Redisson 客户端连接未断,锁就不会提前释放;若连接断开(如网络故障),锁也会按原 TTL 自动过期
加双重保险:锁自带过期 + 主动释放 + 异常兜底
不能只依赖看门狗。生产环境需组合三重防护:
-
加锁带基础 TTL:使用
tryLock(waitTime, leaseTime, TimeUnit),明确设置leaseTime(如 60 秒)。这是最后防线,确保哪怕看门狗失灵,锁也最多占用 60 秒 -
业务层主动释放:在业务逻辑结束处调用
unlock(),并捕获IllegalMonitorStateException(表示当前线程并未持有该锁,可能是已过期或被别人释放) -
异常兜底清理:在全局异常处理器(如 Spring 的
@ControllerAdvice)或关键方法 catch 块中,检查锁是否仍被本线程持有(lock.isHeldByCurrentThread()),若持有则强制 unlock
上线前必须做的验证动作
光写对没用,得验证它真能扛住故障:
- 模拟 JVM 强制退出:在加锁后执行
Runtime.getRuntime().halt(1),观察 Redis 中 key 是否在 TTL 后自动消失(而不是永久存在) - 模拟网络分区:停掉 Redis 节点几秒再恢复,检查看门狗日志是否报错、锁是否正常续期或安全过期
- 压测超时场景:人为让业务逻辑执行时间 > leaseTime,确认看门狗是否持续续期,且最终 unlock 不抛异常











