Redisson的RLock是最省心的分布式锁实现,它基于Redis的SETNX+Lua脚本+自动续期机制,提供线程安全、可重入、带看门狗的锁,避免手动处理超时、死锁和误删问题。

Redisson 的 RLock 是最省心的分布式锁实现
Java 在分布式环境下无法靠 synchronized 或 ReentrantLock 保证跨 JVM 同步,必须依赖外部存储。Redisson 封装了 Redis 的 SETNX + Lua 脚本 + 自动续期机制,直接提供线程安全、可重入、带看门狗(watchdog)的 RLock,避免手动处理锁超时、死锁、误删等问题。
使用前需引入:
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.24.3</version> </dependency>
基础用法示例:
RConfig config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient client = Redisson.create(config);
RLock lock = client.getLock("order:create:20241105");
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
try {
// 执行业务逻辑
} finally {
lock.unlock(); // 自动识别是否本线程加锁,非本线程调用会抛 <code>IllegalMonitorStateException</code>
}
}
-
tryLock(3, 10, SECONDS)表示最多等 3 秒获取锁,锁自动续期默认 30 秒(可通过Config.lockWatchdogTimeout调整) - 若业务执行时间可能超过 30 秒,务必确认看门狗机制已启用(默认开启),否则锁会被其他节点误删
- 不推荐用
lock()无参阻塞式加锁——在 Redis 响应延迟或网络抖动时容易卡死线程
自己用 Redis 实现要注意 SET key value NX PX timeout 的原子性
很多团队想“轻量”自研,但常栽在几个关键点上:不是所有 Redis 客户端都原生支持原子 SET 指令;value 必须是唯一标识(如 UUID + 线程 ID),否则解锁时无法判断所有权;Lua 脚本是唯一能保证「判断+删除」原子性的手段。
立即学习“Java免费学习笔记(深入)”;
解锁 Lua 脚本必须严格校验 value:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
- 不能只用
DEL key,否则 A 加锁、B 超时释放、A 误删 B 的锁 - 不能用
GET + DEL两步操作,中间存在竞态窗口 - 客户端连接中断时,锁只能靠过期时间兜底,所以
PX时间要远大于最大业务耗时(建议 ≥ 3 倍) - Jedis 不直接支持原子 SET,需调用
jedis.set(key, value, "NX", "PX", expireMs);Lettuce 则需用sync().set(key, value, SetArgs.Builder.nx().px(expireMs))
ZooKeeper 的 InterProcessMutex 适合强一致性场景,但运维成本高
当业务对锁的释放实时性要求极高(比如金融对账),且已有 ZooKeeper 集群,可以考虑 Curator 提供的 InterProcessMutex。它基于临时顺序节点 + Watcher 实现,锁释放立即通知等待者,没有 Redis 的过期延迟问题。
但代价明显:
- 每次加锁/解锁都要与 ZK 集群多次交互,性能低于 Redis 方案(实测 QPS 低 3–5 倍)
- ZooKeeper 集群本身需要奇数节点(3/5/7)、对网络分区敏感,故障恢复复杂
- 临时节点失效依赖 session timeout(默认 40 秒),若 GC 停顿超时,锁会意外释放
- Curator 5.x 已废弃
InterProcessMutex,推荐改用DistributedLock,但 API 兼容性需验证
数据库乐观锁仅适用于低并发、单表更新场景
用 UPDATE ... WHERE version = ? 更新并检查影响行数,本质是“冲突检测”而非“锁”,它不阻止并发读,只拒绝脏写。适合库存扣减、订单状态变更等明确主键 + 版本字段的场景。
- 无法保护非 DB 操作(如调用第三方接口、生成文件、发 MQ)
- 高并发下大量 update 失败重试会放大数据库压力,甚至引发雪崩
- 不解决“先查后更”的经典问题——除非配合
SELECT ... FOR UPDATE(这又退化为数据库悲观锁,扩展性差) - MySQL 的
SELECT ... FOR UPDATE在 RR 隔离级别下会加间隙锁,可能导致死锁,需仔细分析执行计划
真正难的从来不是选哪种方案,而是确认你的业务是否真的需要分布式锁——很多所谓“并发问题”,其实用本地缓存 + 最终一致性 + 幂等设计就能绕开。锁只是最后手段,别让锁成为瓶颈本身。










