You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReentrantLock:在finally块解锁时是否需检查锁持有者?

问题分析与解决方案

原代码的核心问题

你的代码在fetchNewToken延迟高时频繁卡顿10秒并抛出大量异常,主要有两个原因:

  1. 锁释放逻辑错误:try块中已经调用了lock.unlock(),但finally块又重复执行解锁操作——此时当前线程已经不持有锁,调用unlock()会直接抛出IllegalMonitorStateException,而且你的while循环会反复触发这个异常,消耗线程资源。
  2. 锁竞争超时:当一个线程持有锁调用fetchNewToken(耗时1秒),其他线程调用tryLock会等待10秒(你的LOCK_TIMEOUT_SEC值)后超时抛出AppException;如果锁没有被正确释放,会导致更多线程陷入等待超时的循环。

关于finally块的疑问解答

1. 是否需要检查锁持有者?

必须检查。ReentrantLock的核心规则是:只有持有锁的线程才能调用unlock(),否则会抛出IllegalMonitorStateException。如果不做检查,无论当前线程是否持有锁都执行解锁,必然会触发大量异常,甚至破坏锁的并发语义。

2. 其他线程持有锁时能否解锁?

绝对不能。锁的作用是保证同一时间只有一个线程执行临界区代码,如果允许非持有线程解锁,会直接导致临界区代码被多个线程同时执行,引发数据不一致或其他并发问题。

3. 为什么查到的示例没做这个检查?

多数示例会用标记变量记录是否成功获取锁,再结合锁的持有判断,只是写法不同。比如用boolean acquired = false,在成功获取锁后设为true,finally块只在acquired为true时解锁——本质上和检查当前线程持有锁是互补的逻辑,都是为了避免非法解锁。

修正后的代码

public String getToken(String userId, ReentrantLock lock) throws InterruptedException {
    boolean isLockAcquired = false;
    try {
        // 尝试获取锁,超时则抛出异常
        if (!lock.tryLock(LOCK_TIMEOUT_SEC, TimeUnit.SECONDS)) {
            throw new AppException("Could not get lock in " + LOCK_TIMEOUT_SEC + "s");
        }
        isLockAcquired = true;
        // 调用外部接口获取token
        String token = fetchNewToken(userId);
        return token;
    } finally {
        // 仅当当前线程持有锁且标记为已获取时,才执行解锁
        if (isLockAcquired && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

关键优化点

  • 用isLockAcquired标记是否成功获取锁,避免在未拿到锁的情况下执行解锁。
  • 将解锁逻辑统一放在finally块,确保无论fetchNewToken正常返回还是抛出异常,锁都会被正确释放。
  • 结合lock.isHeldByCurrentThread()做双重校验,避免极端情况下标记与实际锁状态不一致的问题。

内容的提问来源于stack exchange,提问作者WesternGun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:05:59