Redisson分布式锁详解:tryLock参数深度解析
概述
本文档详细解析Redisson分布式锁中tryLock()方法的核心参数,重点说明等待时间和锁过期时间的工作机制,帮助开发者正确理解和使用分布式锁。
📚 官方文档参考: Redisson官方文档 - 建议配合阅读以获取更全面的信息
核心代码示例
RLock lock = redissonClient.getLock("order:create:" + payAccount);
boolean ok = false;
try {
// 5 秒内尝试获取锁,成功后 30 秒后自动释放(除非提前 unlock)
ok = lock.tryLock(5, 30, TimeUnit.SECONDS);
if (!ok) {
// 业务自定义:直接返回或抛异常
return Result.error("系统繁忙,请稍后再试");
}
// === 临界区开始 ===
orderService.createOrder(orderRequest);
// === 临界区结束 ===
return Result.OK("创建订单成功");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("获取分布式锁被中断", e);
} finally {
// 按您当前规范安全释放
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
tryLock参数详解
方法签名
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException
参数1:waitTime = 5 秒(等待获取锁的最大时间)
核心含义: 当前线程最多等待5秒来尝试获取锁
详细机制:
- 立即尝试:首先立即尝试获取锁,如果锁当前可用,直接获取成功
- 等待重试:如果锁被其他线程持有,当前线程会进入等待状态
- 轮询机制:在5秒内,Redisson会周期性地重试获取锁(通常每隔几毫秒到几十毫秒)
- 超时返回:如果5秒内仍无法获取到锁,方法返回
false - 中断响应:等待过程中如果线程被中断,会抛出
InterruptedException
实际场景:
// 场景1:锁立即可用
Thread A: tryLock(5, 30, SECONDS) → 立即返回 true(耗时 < 1ms)
// 场景2:需要等待
Thread A: 持有锁,执行业务逻辑
Thread B: tryLock(5, 30, SECONDS) → 等待2秒后Thread A释放锁 → 返回 true
// 场景3:等待超时
Thread A: 持有锁,执行长时间业务逻辑
Thread B: tryLock(5, 30, SECONDS) → 等待5秒仍未获取到锁 → 返回 false
参数2:leaseTime = 30 秒(锁的自动过期时间)
核心含义: 获取锁成功后,锁将在30秒后自动释放,即使没有手动调用unlock()
详细机制:
- 看门狗失效:设置了leaseTime后,Redisson的看门狗(watchdog)机制会被禁用
- 固定过期:锁的过期时间被固定为30秒,不会自动续期
- 兜底保护:防止因为程序异常、服务器宕机等导致锁永远不被释放
- Redis实现:在Redis中通过
EXPIRE命令设置键的过期时间
看门狗机制对比:
// 不指定leaseTime(启用看门狗)
lock.tryLock(5, TimeUnit.SECONDS); // 默认30秒,但会自动续期
// 指定leaseTime(禁用看门狗)
lock.tryLock(5, 30, TimeUnit.SECONDS); // 固定30秒,不会续期
续期机制说明:
- 启用看门狗时:每隔
leaseTime/3的时间(默认10秒)自动续期 - 禁用看门狗时:到达30秒后直接过期,无论业务是否完成
执行时序图
时间轴: 0s ----5s----10s----15s----20s----25s----30s----35s
Thread A: [等待2s] [获取锁成功] [执行业务逻辑] [手动释放锁]
Thread B: [等待5s超时] [返回false]
Thread C: [等待] [获取锁成功]
锁状态: [被占用] [Thread A持有] [释放] [Thread C持有]
参数选择最佳实践
waitTime(等待时间)选择指南
短等待时间(1-5秒)适用场景:
- 高并发场景,需要快速失败
- 用户体验敏感的接口
- 非关键业务操作
长等待时间(10-30秒)适用场景:
- 重要业务操作,允许适当等待
- 锁竞争不激烈的场景
- 批处理任务
leaseTime(过期时间)选择指南
短过期时间(10-30秒)适用场景:
- 业务执行时间可预期且较短
- 高并发场景,减少锁占用时间
- 对一致性要求极高的场景
长过期时间(60-300秒)适用场景:
- 复杂业务逻辑,执行时间较长
- 涉及外部系统调用的操作
- 数据处理或报表生成任务
常见问题与解决方案
问题1:业务执行时间超过leaseTime
问题现象:
// 业务逻辑执行了45秒,但锁30秒就过期了
ok = lock.tryLock(5, 30, TimeUnit.SECONDS);
if (ok) {
// 这里执行了45秒
complexBusinessLogic(); // 可能导致重复执行
}
解决方案:
// 方案1:预估业务时间,设置合适的leaseTime
ok = lock.tryLock(5, 60, TimeUnit.SECONDS); // 增加到60秒
// 方案2:使用看门狗机制
ok = lock.tryLock(5, TimeUnit.SECONDS); // 不指定leaseTime,启用自动续期
// 方案3:手动续期
if (lock.remainTimeToLive() < 10000) { // 剩余时间少于10秒
lock.expire(30, TimeUnit.SECONDS); // 手动续期30秒
}
问题2:等待时间过短导致频繁失败
问题现象:
// waitTime设置过短,在高并发下频繁返回false
ok = lock.tryLock(1, 30, TimeUnit.SECONDS); // 1秒可能太短
解决方案:
// 合理设置等待时间,或使用指数退避重试
int maxRetries = 3;
int baseWaitTime = 2;
for (int i = 0; i < maxRetries; i++) {
int waitTime = baseWaitTime * (int) Math.pow(2, i); // 2, 4, 8秒
ok = lock.tryLock(waitTime, 30, TimeUnit.SECONDS);
if (ok) break;
}
问题3:锁释放的安全性
推荐的释放模式:
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
检查条件说明:
lock != null:确保锁对象存在lock.isLocked():确保锁仍然被持有lock.isHeldByCurrentThread():确保是当前线程持有的锁
监控和日志建议
long startTime = System.currentTimeMillis();
boolean ok = lock.tryLock(5, 30, TimeUnit.SECONDS);
long waitDuration = System.currentTimeMillis() - startTime;
if (ok) {
log.info("获取锁成功,等待时间:{}ms,锁标识:{}", waitDuration, lockKey);
try {
// 业务逻辑
long businessStartTime = System.currentTimeMillis();
orderService.createOrder(orderRequest);
long businessDuration = System.currentTimeMillis() - businessStartTime;
log.info("业务执行完成,耗时:{}ms", businessDuration);
} finally {
lock.unlock();
log.info("锁已释放,锁标识:{}", lockKey);
}
} else {
log.warn("获取锁失败,等待超时:{}ms,锁标识:{}", waitDuration, lockKey);
}
总结
- 参数5(waitTime):控制获取锁的耐心程度,平衡响应速度与成功率
- 参数30(leaseTime):控制锁的安全释放,防止死锁但需匹配业务执行时间
- 核心原则:waitTime关注用户体验,leaseTime关注系统安全
- 调优建议:根据具体业务场景和性能监控数据,动态调整这两个参数