4. Redis实现Lock接口
基于 Redis 实现 Java Lock 接口的设计与实现文档
一、背景与目标
在分布式系统中,为了保证多个节点对共享资源访问的一致性和互斥性,往往需要引入分布式锁机制。Redis 由于其性能优秀、支持原子操作且具备良好的生态,被广泛应用于实现分布式锁的场景。
本文旨在介绍如何通过 Redis 来实现 Java 的 Lock
接口,并深入探讨不同的实现方式、其原理、优缺点与扩展思路。
二、为什么 Redis 非常适合用于实现分布式锁?
Redis 在以下多个方面天然适配于分布式锁的实现:
1. 原子操作
- Redis 提供的
SET key value NX PX
命令具备原子性。 - 同一时间只有一个客户端能设置成功,从而实现互斥。
2. 快速响应(低延迟)
- Redis 是内存数据库,读写速度极快,适合高频加解锁操作。
3. 丰富的操作语义
- 除了
SET NX PX
,Redis 支持 Lua 脚本,可以原子化处理复杂的检查+释放逻辑,避免 race condition。
4. 自然支持过期机制
- Redis 可为 key 设置 TTL(Time to Live),防止客户端 crash 导致锁永远无法释放,即“死锁”。
5. 客户端丰富,生态完善
- Java 中可用 Jedis、Lettuce、Redisson 等多个客户端,并支持与 Spring 框架高度集成。
6. 支持持久化与高可用
- Redis 支持主从复制、哨兵机制、集群部署等,可以支持更高的可用性和容灾能力。
7. 简单易用
- 相比 ZooKeeper,Redis 的部署成本更低,学习曲线平滑,在非强一致性场景下性价比更高。
三、目标与实现思路
我们希望实现如下目标:
- 使用 Redis 实现
java.util.concurrent.locks.Lock
接口。 - 支持基本的
lock()
、unlock()
、tryLock()
方法。 - 支持阻塞、超时、自动过期等机制。
- 可扩展以支持公平锁、可重入锁等功能。
Redis 本身支持如下两种机制来构建分布式锁:
- 基于
SET key value NX PX
命令实现基本锁 - 基于 Redisson(推荐)或实现 Redlock 算法
四、Redis 分布式锁核心原理
1. 基本命令方式
使用 Redis 的 SET
命令实现原子加锁:
SET lock_key unique_value NX PX 30000
含义:
NX
表示只有当 key 不存在时才设置成功,相当于加锁。PX
表示过期时间,防止死锁。unique_value
一般为 UUID,表示当前线程唯一标识。
释放锁使用 Lua 脚本确保原子性:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
2. Redlock 算法(多 Redis 节点)
由 Redis 作者提出,适用于高可用场景。要求:
- 向多个 Redis 实例写入锁
- 获得超过半数成功则视为加锁成功
- 使用时需注意时钟漂移、网络抖动等问题
推荐使用 Redisson 等成熟组件代替手写实现。
五、RedisLock 实现类的详细实现流程
1. 构造函数
- 传入 RedisTemplate 与 lockKey。
- 初始化
ThreadLocal<String>
用于保存本线程生成的唯一锁标识。
2. lock()
实现逻辑
- 通过
tryLock()
自旋尝试加锁。 - 若失败,则
Thread.sleep
一段时间后重试,直到成功。
3. tryLock()
实现逻辑
- 使用 UUID 作为当前线程锁的唯一标识。
- 通过 Redis 的
SET NX PX
尝试设置 key。 - 若成功,保存 UUID 到线程变量中。
- 若失败,返回 false。
4. unlock()
实现逻辑
- 获取当前线程存储的 UUID。
- 使用 Lua 脚本判断当前 key 是否由本线程持有。
- 若是则释放;否则不做处理。
5. 不支持的方法处理
lockInterruptibly
、tryLock(timeout)
、newCondition
方法暂时抛出UnsupportedOperationException
。
6. 锁值隔离说明
ThreadLocal
保证多线程环境下 UUID 隔离。- 避免线程 A 设置了锁,线程 B 意外解锁的情况。
六、基本实现(自定义 RedisLock)
public class RedisLock implements Lock {
private final String lockKey;
private final RedisTemplate<String, String> redisTemplate;
private final ThreadLocal<String> lockValue = new ThreadLocal<>();
public RedisLock(String lockKey, RedisTemplate<String, String> redisTemplate) {
this.lockKey = lockKey;
this.redisTemplate = redisTemplate;
}
@Override
public void lock() {
while (!tryLock()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}
@Override
public boolean tryLock() {
String uuid = UUID.randomUUID().toString();
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(lockKey, uuid, Duration.ofSeconds(30));
if (Boolean.TRUE.equals(success)) {
lockValue.set(uuid);
return true;
}
return false;
}
@Override
public void unlock() {
String value = lockValue.get();
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey),
value
);
lockValue.remove();
}
@Override public void lockInterruptibly() throws InterruptedException { throw new UnsupportedOperationException(); }
@Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); }
@Override public Condition newCondition() { throw new UnsupportedOperationException(); }
}
七、扩展方式与改进思路
1. 可重入锁实现
- 通过
ThreadLocal
记录当前线程持有锁次数 - Redis 中的 value 存储 JSON:threadId + count
- 释放时进行 count–
2. 公平锁
- 使用 Redis List 模拟等待队列
- 每次加锁都判断是否轮到当前线程
3. Watch Dog 自动续期
- 每隔一定时间判断锁是否持有
- 自动续约过期时间,避免因业务时间长导致锁被抢走
Redisson 实现中使用 ScheduledExecutorService
来实现
4. 集群下支持(Redlock)
- 建议使用 Redisson,支持 Redlock
- 若自定义实现,需记录多个节点响应时延和分布式时间偏差
八、开源方案推荐
名称 | 说明 |
---|---|
Redisson | 支持 ReentrantLock、ReadWriteLock、Semaphore、CountDownLatch 等多种同步原语 |
Lettuce | 提供较低级的 Redis 操作能力,适用于自定义实现 |
Jedis | 经典 Redis 客户端,可支持基础锁实现 |
九、总结
- Redis 是实现分布式锁的理想选择,适合场景如定时任务调度、单点任务控制、共享资源互斥访问等。
- 基于 Redis 的
SET NX PX
命令+Lua 脚本可实现原子性的加解锁。 - 实现
Lock
接口需关注:唯一标识、过期时间、线程隔离性、可扩展性。 - 推荐使用 Redisson 等成熟方案,在开发效率、可靠性、扩展性方面优势明显。
附录:参考文献
- Redis 官方文档:https://redis.io
- Redisson GitHub:https://github.com/redisson/redisson
- Redis distributed locks: https://redis.io/docs/manual/patterns/distributed-locks/
- Redlock Algorithm: https://redis.io/docs/reference/patterns/distributed-locks/#the-redlock-algorithm