问题模拟
实际问题简化为 DEMO 如下
@SneakyThrows
@GetMapping("/lockA")
public void lockA() {
RLock lockA = redissonClient.getLock("TEST_LOCK_A");
log.info("尝试获取锁 TEST_LOCK_A");
if (lockA.tryLock()) {
log.info("获取锁 TEST_LOCK_A 成功");
for (int i = 0; i < 10; i++) {
log.info("持有锁 TEST_LOCK_A 10 秒,当前第 {} 秒", i + 1);
Thread.sleep(1000);
}
lockA.unlock();
log.info("释放锁 TEST_LOCK_A");
} else {
log.info("获取锁 TEST_LOCK_A 失败");
}
}
@SneakyThrows
@GetMapping("/lockAB")
public void lockAB() {
RedissonMultiLock multiLock = new RedissonMultiLock(
Stream.of("TEST_LOCK_A", "TEST_LOCK_B")
.map(redissonClient::getLock)
.toArray(RLock[]::new));
log.info("尝试获取锁 TEST_LOCK_A 和 TEST_LOCK_B");
if (multiLock.tryLock(1, -1, TimeUnit.SECONDS)) {
log.info("获取锁 TEST_LOCK_A 和 TEST_LOCK_B 成功");
for (int i = 0; i < 5; i++) {
log.info("持有锁 TEST_LOCK_A 和 TEST_LOCK_B 5 秒,当前第 {} 秒", i + 1);
Thread.sleep(1000);
}
multiLock.unlock();
log.info("释放锁 TEST_LOCK_A 和 TEST_LOCK_B");
} else {
log.info("获取锁 TEST_LOCK_A 和 TEST_LOCK_B 失败");
multiLock.unlock();
}
}
接口单独运行
lockA:

lockAB:

接口竞争运行(A 未释放时通过 lockAB 接口尝试获取 AB 锁):

问题位置:

注释后运行结果:

总结
实际情况往往会较 DEMO 隐蔽许多。
锁的获取和释放是严格一一对应的,多数锁操作后通常都使用 try-catch-finally 进行异常处理,在异常情况下对锁进行释放。哪怕在多层嵌套逻辑下,也要确保在 finally 中进行 unlock 时,仅对已获取到的锁进行释放。