分布式锁-Redisson 可重入锁
在分布式系统中,分布式锁是一种常用的机制,用来在多个实例或服务之间控制共享资源的访问,确保不会出现并发冲突或资源竞争。Redis 是常用的分布式锁实现工具,而 Redisson 是 Redis 的一个 Java 客户端,它提供了强大且易用的分布式锁功能,包括可重入锁、读写锁、公平锁等。
1. 什么是可重入锁
可重入锁(Reentrant Lock),也称为递归锁,是指同一个线程在持有锁的情况下,可以再次获取该锁而不会被锁住。这种锁允许线程重复进入被它锁定的代码块,避免死锁问题。
可重入锁的一个典型应用场景是,某个方法在持有锁时,调用了另一个也需要获取该锁的方法。如果没有可重入特性,这种情况下将会造成死锁。
在 Redisson 中,可重入锁的核心功能是允许线程在持有锁的情况下,能够多次请求同一个锁,而不需要手动释放锁多次。
2. Redisson 可重入锁的特点
- 可重入性:同一线程可以多次获取同一个锁,锁的计数器会递增。
- 自动续期:默认情况下,Redisson 的锁会自动为持有锁的线程续期,确保锁在超时前不会被意外释放。
- 高可用:通过 Redis 集群,Redisson 的分布式锁可以在分布式环境中保证高可用性,支持故障恢复和锁状态的持久化。
3. Redisson 可重入锁的使用
3.1 引入 Redisson 依赖
在 Spring Boot 项目或其他 Java 项目中使用 Redisson,可以通过 Maven 引入 Redisson 的依赖:
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.4</version>
</dependency>
3.2 配置 Redisson 客户端
Redisson 需要连接 Redis 服务器。可以通过 redisson.yaml
文件进行配置,或在代码中手动配置 Redis 连接。
使用 YAML 文件进行配置(redisson.yaml
):
singleServerConfig:address: "redis://127.0.0.1:6379"password: nulldatabase: 0
在 Spring Boot 中加载该配置文件:
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {Config config = Config.fromYAML(RedissonConfig.class.getClassLoader().getResource("redisson.yaml"));return Redisson.create(config);}
}
3.3 使用 Redisson 实现可重入锁
一旦 Redisson 客户端配置好,就可以在代码中使用 Redisson 提供的分布式锁 API。下面是使用可重入锁的示例:
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class LockService {@Autowiredprivate RedissonClient redissonClient;public void executeWithLock() {// 获取分布式可重入锁RLock lock = redissonClient.getLock("myLock");try {// 尝试加锁,等待 10 秒,10 秒后自动释放if (lock.tryLock(10, 10, TimeUnit.SECONDS)) {try {// 执行业务逻辑System.out.println("业务逻辑执行中...");// 模拟内部调用方法,重入锁nestedLockMethod();} finally {// 释放锁lock.unlock();}}} catch (InterruptedException e) {e.printStackTrace();}}// 模拟重入锁的情况private void nestedLockMethod() {RLock lock = redissonClient.getLock("myLock");lock.lock();try {System.out.println("内部方法执行...");} finally {lock.unlock();}}
}
在上述代码中:
redissonClient.getLock("myLock")
获取一个可重入锁。tryLock(10, 10, TimeUnit.SECONDS)
表示尝试获取锁,最多等待 10 秒,如果获取成功,锁将在 10 秒后自动释放。- 在
nestedLockMethod()
方法中,模拟了重入锁的情况,同一个线程再次获取了同一个锁,而不会出现阻塞。
3.4 自动续期机制
Redisson 的分布式锁有一个非常重要的功能:自动续期。当一个线程获取了锁后,Redisson 会默认启动一个守护线程,为该锁的有效期进行续期,防止锁在执行长时间任务时被自动释放。
默认情况下,Redisson 会在锁被持有时每隔 30 秒续期,确保锁不会因为超时而被误释放。如果业务逻辑执行时间较长,这个功能可以防止锁过期失效。
自动续期的实现方式是,Redisson 在后台定期检查持有锁的线程是否仍然存活,如果线程仍在运行,它会延长锁的过期时间。
3.5 手动设置锁的超时时间
尽管 Redisson 提供了自动续期功能,但在某些场景下,开发者可能希望手动设置锁的有效时间。可以通过 lock()
方法的重载版本设置锁的超时时间:
// 设置锁的自动释放时间为 30 秒
lock.lock(30, TimeUnit.SECONDS);
这种方式适合业务逻辑执行时间较短且可以预估的场景。如果在设定的超时时间内没有释放锁,Redisson 会自动释放该锁。
4. Redisson 可重入锁的工作原理
4.1 Redis 中的锁实现
Redisson 的可重入锁是基于 Redis 的 SET
命令和 Lua
脚本实现的。当线程获取锁时,Redisson 会在 Redis 中执行类似如下的命令:
SET myLock <random-value> NX PX 10000
NX
:表示只有当键不存在时才会设置,确保分布式锁的原子性。PX
:设置锁的有效期(过期时间),单位为毫秒。
可重入锁通过给每个线程生成一个唯一的标识(random-value
)来区分不同线程的锁。如果同一个线程多次获取锁,Redisson 会使用计数器来跟踪锁的重入次数,释放锁时只会在计数器归零时才真正释放。
4.2 解锁的实现
解锁操作也需要保证线程安全,Redisson 通过 Lua 脚本确保只有持有锁的线程才能成功解锁。Lua 脚本可以保证解锁操作的原子性:
if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
elsereturn 0
end
在解锁时,Redisson 会检查 Redis 中存储的锁标识是否与当前线程匹配,只有匹配时才会删除锁。
5. Redisson 可重入锁的应用场景
可重入锁在以下场景中非常有用:
- 分布式资源争用:多个服务实例或线程同时访问共享资源,如数据库、文件系统、第三方服务等,使用分布式锁可以确保这些资源不会被并发修改。
- 任务调度和执行:在分布式任务调度系统中,确保同一时间只有一个实例在处理某个任务,防止任务重复执行。
- 库存管理和订单处理:在电商系统中,避免多个请求同时操作同一件商品的库存,导致超卖问题。
6. 注意事项
- 锁的自动续期:Redisson 默认的自动续期功能适合大部分场景,但开发者应确保持有锁的业务逻辑不会意外地被卡住或进入死循环。
- 合理设置超时时间:在使用
tryLock()
或手动设置锁的超时时间时,应根据业务需求合理设置,避免锁的有效期太长或太短。 - 持久化与高可用:确保 Redis 服务器的高可用性,Redisson 锁依赖 Redis 的可靠性,建议使用 Redis 集群或主从架构。
7.
总结
Redisson 为 Java 开发者提供了强大的分布式锁功能,特别是可重入锁的实现,使得在分布式系统中可以轻松实现线程间和服务实例间的资源竞争控制。通过 Redisson 的可重入锁,开发者可以确保同一线程在获取锁后能够多次进入锁定区域,避免死锁。同时,Redisson 的自动续期功能确保了锁在长时间任务中的可靠性。配合 Redis 的高性能和高可用性,Redisson 是分布式环境中实现锁机制的理想选择。