问题模拟
实际问题简化为 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 时,仅对已获取到的锁进行释放。