redisson基础
介绍
官方定义:Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。
简单来讲Redisson是一个在Redis的基础上实现的分布式工具的集合。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。
Redisson实现分布式锁
- 引入redisson依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.45.1</version></dependency>
温馨提示:此外还有一种引入方式,直接引入redission的starter依赖,然后在yam|文件中配置Redisson,但是不推荐这种方式,因为他会替换掉Spring官方提供的这套对 Redisson 的配置。所以我们采用@Bean手动配置。
- RedissonConfig:配置Redisson客户端
/*** Redisson配置类*/
@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {// 配置类Config config = new Config();// 添加redis地址,这里添加了单点的地址,也可以使用config.useClusterServers()添加集群地址config.useSingleServer().setAddress("redis://192.168.8.100:6379").setPassword("123321");// 创建RedissonClient客户端对象return Redisson.create(config);}
}
以下是 Redisson 中 lock()
和 tryLock()
方法所有重载形式的详细解析,重点说明其是否支持看门狗机制,并对比不同方法的适用场景。
一、lock()
方法详解
1. 无参形式:void lock()
- 行为:无限阻塞直到获取锁,线程会一直等待,直到锁可用或线程被中断。
- 看门狗机制:✅ 支持
默认启用看门狗,锁的初始租期为 30 秒,每隔 10 秒(租期的 1/3)自动续期一次,确保业务未完成时锁不会过期。 - 适用场景:
- 需要严格保证互斥性的场景(如金融交易、库存扣减)。
- 无法预估业务耗时,需依赖自动续期避免锁过期。
public void criticalTask() {RLock lock = redisson.getLock("criticalLock");lock.lock();try {// 执行耗时不确定的核心业务processUnpredictableTask();} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}} }
2. 带租期参数:void lock(long leaseTime, TimeUnit unit)
-
行为:获取锁后,锁在
leaseTime
时间后自动释放,无论业务是否完成。 -
看门狗机制:❌ 不支持
显式指定leaseTime
会禁用看门狗,锁到期后强制释放,可能导致业务未完成时锁失效。 -
适用场景:
- 明确知道业务最大耗时,且能保证在
leaseTime
内完成。 - 需要避免看门狗线程资源开销的场景。
public void timeBoundedTask() {RLock lock = redisson.getLock("timedLock");lock.lock(60, TimeUnit.SECONDS); // 60秒后自动释放try {// 必须在60秒内完成的任务processTimeSensitiveTask();} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}} }
- 明确知道业务最大耗时,且能保证在
二、tryLock()
方法详解
1. 无参形式:boolean tryLock()
-
行为:立即尝试获取锁,成功返回
true
,失败返回false
,不阻塞线程。 -
看门狗机制:✅ 支持
默认启用看门狗,锁租期为 30 秒,自动续期。 -
适用场景:
- 非阻塞快速失败场景,如高并发下的缓存重建。
- 需要快速响应,避免线程阻塞。
public void cacheRebuild(String key) {RLock lock = redisson.getLock("cacheLock:" + key);if (lock.tryLock()) {try {// 重建缓存(耗时可控)rebuildCache(key);} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}}} else {// 其他线程已处理,直接返回log.info("缓存正在重建中...");} }
2. 限时等待:boolean tryLock(long waitTime, TimeUnit unit)
- 行为:在
waitTime
时间内尝试获取锁,超时返回false
,支持线程中断。 - 看门狗机制:✅ 支持
锁租期默认 30 秒,自动续期。 - 适用场景:
- 允许短暂等待的并发控制,如订单处理。
- 平衡系统吞吐量与资源争用。
public void processOrder(String orderId) {RLock lock = redisson.getLock("orderLock:" + orderId);try {if (lock.tryLock(5, TimeUnit.SECONDS)) { // 最多等待5秒try {// 处理订单handleOrder(orderId);} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}}} else {throw new BusiException("系统繁忙,请重试");}} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new BusiException("操作被中断");}}
3. 双重时间控制:boolean tryLock(long waitTime, long leaseTime, TimeUnit unit)
- 行为:在
waitTime
时间内尝试获取锁,成功后锁持有leaseTime
时间。 - 看门狗机制:❌ 不支持
显式指定leaseTime
会禁用看门狗。 - 适用场景:
- 精确控制锁生命周期,如定时任务。
- 已知业务耗时的场景,避免自动续期开销。
public void scheduledJob() {RLock lock = redisson.getLock("scheduledJobLock");try {// 等待10秒获取锁,持有锁30秒if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {try {// 执行30秒内必须完成的定时任务executeScheduledTask();} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}}}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
三、方法对比与选型指南
1. 看门狗机制支持对比
方法签名 | 看门狗支持 | 阻塞行为 | 锁持有时间控制 |
---|---|---|---|
void lock() | ✅ | 无限阻塞 | 自动续期 |
void lock(long leaseTime, TimeUnit unit) | ❌ | 无限阻塞 | 固定时间 |
boolean tryLock() | ✅ | 非阻塞 | 自动续期 |
boolean tryLock(long waitTime, ...) | ✅ | 限时阻塞 | 自动续期 |
boolean tryLock(long waitTime, leaseTime, ...) | ❌ | 限时阻塞 | 固定时间 |
2. 适用场景对比
场景特性 | 推荐方法 | 理由 |
---|---|---|
严格互斥,耗时不确定 | lock() | 依赖看门狗自动续期,避免锁过期导致数据不一致。 |
已知耗时,需固定锁时间 | lock(leaseTime, unit) | 显式控制锁生命周期,避免看门狗线程开销。 |
非阻塞快速失败 | tryLock() | 立即返回结果,适用于高并发下的缓存击穿防护。 |
允许短暂等待,自动续期 | tryLock(waitTime, unit) | 平衡等待时间和自动续期,适合订单处理等常见业务。 |
精确时间控制,禁用看门狗 | tryLock(waitTime, leaseTime, ...) | 适用于定时任务或批处理,需确保锁在固定时间释放。 |
四、最佳实践与风险规避
1. 看门狗机制的风险控制
-
风险:若业务逻辑卡死(如死循环),看门狗会无限续期,导致锁无法释放。
-
规避:
// 设置合理的看门狗超时时间(默认30秒) Config config = new Config(); config.setLockWatchdogTimeout(60_000); // 调整为60秒 RedissonClient redisson = Redisson.create(config);
2. 固定租期(leaseTime
)的陷阱
- 风险:若业务耗时超过
leaseTime
,其他线程可能提前获取锁,导致数据冲突。 - 规避:
// 确保 leaseTime > 业务最大耗时 + 缓冲时间(如20%) long maxBusinessTime = 5000; // 业务最大耗时5秒 lock.tryLock(0, maxBusinessTime * 1200, TimeUnit.MILLISECONDS); // 设置6秒
3. 高并发场景优化
- 问题:大量线程同时争抢锁可能导致 Redis 压力激增。
- 优化:结合随机退避和本地锁:
// 使用本地锁减少Redis请求 private final ConcurrentHashMap<String, Object> localLocks = new ConcurrentHashMap<>();public void highConcurrencyTask(String key) {Object localLock = localLocks.computeIfAbsent(key, k -> new Object());synchronized (localLock) {RLock redisLock = redisson.getLock(key);if (redisLock.tryLock(50, TimeUnit.MILLISECONDS)) {try {// 处理业务} finally {redisLock.unlock();}}} }
通过合理选择锁方法,结合业务场景和看门狗机制的特性,可以在保证数据一致性的同时,最大化系统性能和可靠性。