锁释放问题

问题模拟

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