在分布式系统中,多个服务节点需要对共享资源进行协调访问。Redisson 是一个基于 Redis 的 Java 客户端库,它不仅提供了丰富的数据结构支持,还实现了多种分布式锁机制,如可重入锁、读写锁、联锁等。
本文将从源码角度分析 Redisson 中 `RLock` 接口的核心实现类 `RedissonLock`,深入探讨其可重入、自动续期、公平锁、Redlock 算法支持等特性,并结合实际应用场景给出使用建议。
一、Redisson 分布式锁核心接口:RLock
Redisson 提供了 `RLock` 接口,继承自 JDK 的 `
java.util.concurrent.locks.Lock`,并扩展了支持 Redis 的特性:
public interface RLock extends Lock, RExpirable {
void lock(long leaseTime, TimeUnit unit);
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
...
}
主要实现类包括:
● `RedissonLock`:基础可重入锁;
● `RedissonReadLock` / `RedissonWriteLock`:读写锁;
● `RedissonMultiLock`:多锁组合(Redlock 实现);
● `RedissonFairLock`:公平锁。
二、RedissonLock 的基本结构
`RedissonLock` 是 Redisson 分布式锁的核心实现类,其关键字段如下:
private final CommandAsyncExecutor commandExecutor;
private final String id; // UUID + threadId,唯一标识持有线程
private final String name; // 锁名称 key
private final int internalLockLeaseTime; // 默认加锁时间(毫秒)
底层通过 Lua 脚本操作 Redis 来保证原子性,确保加锁和解锁过程的线程安全。
三、加锁流程详解:tryLockInnerAsync 方法
加锁的核心方法是 `tryLockInnerAsync`,使用 Lua 脚本执行:
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Arrays.<Object>asList(getName()),
unit.toMillis(leaseTime), getLockName(threadId));
}
脚本逻辑说明:
1. 如果锁不存在(`exists key == 0`),则设置 hash 表中的当前线程 ID 计数为 1,并设置过期时间;
2. 如果当前线程已持有锁,则增加计数器(实现可重入);
3. 否则返回当前锁的剩余生存时间(TTL)。
该脚本保证了 Redis 中锁的原子性获取与重入逻辑。
四、自动续期机制:Watchdog
Redisson 支持自动续期功能,默认加锁时间为 30 秒(可通过 `lockWatchdogTimeout` 配置),并在后台启动 Watchdog 线程定期刷新锁的 TTL。
private void scheduleExpirationRenewal(long threadId) {
Timeout task = commandExecutor.getConnectionManager().newTimeout(timeout -> {
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock expiration", e);
return;
}
if (res) {
// 成功续期后继续调度
scheduleExpirationRenewal(threadId);
}
});
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
每 1/3 锁超时时间检查一次,若锁仍被当前线程持有,则调用 `renewExpirationAsync` 续期。
五、释放锁流程:unlockInnerAsync
释放锁也通过 Lua 脚本实现:
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
"redis.call('del', KEYS[1]); " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end;",
Arrays.<Object>asList(getName(), getChannelName()),
LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
逻辑如下:
1. 判断当前线程是否持有锁;
2. 减少重入计数器;
3. 若计数器大于 0,仅更新 TTL;
4. 若为 0,删除锁并发布解锁消息通知等待线程。
六、公平锁实现:RedissonFairLock
Redisson 还提供了公平锁的实现 `RedissonFairLock`,其核心在于维护一个队列,确保线程按顺序获取锁。
关键机制包括:
● 使用 Redis List 结构模拟 FIFO 队列;
● 加锁前检查是否轮到自己;
● 自动清理无效节点避免死锁。
七、Redlock 算法支持:RedissonMultiLock
Redisson 支持 Redlock 算法,通过 `RedissonMultiLock` 将多个独立的 Redis 实例上的锁组合成一个锁,提高可用性和一致性。
RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
lock.lock();
其内部实现采用 N/2+1 个节点成功即认为加锁成功 的策略,符合 Redlock 算法要求。
八、适用场景与最佳实践
最佳实践建议:
● 合理设置锁超时时间,避免死锁;
● 避免长时间持有锁,防止阻塞其他线程;
● 优先使用异步 API,提升性能;
● 监控 Redis 性能与连接状态,确保锁服务可用性。
九、总结
Redisson 的分布式锁设计充分考虑了可重入、公平性、自动续期、集群支持等多个维度,是一个成熟、稳定的分布式协调工具。其核心特点如下:
通过对 Redisson 源码的深入理解,开发者可以更好地掌握其内部机制,在实际项目中高效地使用分布式锁,提升系统的稳定性与一致性。
> 参考资料:
> - [Redisson GitHub](https://github.com/redisson/redisson)
> - Redisson 官方文档
> - 《Redis 设计与实现》
> - Martin Fowler 对 Redlock 的讨论文章