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

线程中断异常下Semaphore资源释放问题求助

解决方案:安全管理Semaphore的资源获取与释放

你的问题核心在于未准确跟踪已成功获取的资源,导致在中断或获取失败时错误地释放未持有的Semaphore许可——这确实会破坏Semaphore的计数逻辑,引发后续线程的资源获取异常。下面给出两种可靠的解决思路:

方法一:用标志位跟踪资源获取状态

通过布尔变量记录每个Semaphore是否成功获取,在finally块中仅释放已获取的资源,同时正确处理中断状态:

public void run() {
    boolean hasA = false;
    boolean hasB = false;
    while (!Thread.currentThread().isInterrupted()) {
        try {
            // 先获取A,成功后标记状态
            A.acquire(2);
            hasA = true;
            // 再获取B,成功后标记状态
            B.acquire(2);
            hasB = true;
            
            // 执行你的业务逻辑 some-stuff
            doBusinessLogic();
            
        } catch (InterruptedException ex) {
            // 恢复中断标志,让循环能正确退出
            Thread.currentThread().interrupt();
        } finally {
            // 按逆序释放资源(避免死锁风险),仅释放已获取的
            if (hasB) {
                B.release(2);
                hasB = false;
            }
            if (hasA) {
                A.release(2);
                hasA = false;
            }
        }
    }
}

关键细节:

  • 逆序释放:和获取顺序相反,这是多资源同步的通用最佳实践,能降低死锁概率。
  • 恢复中断状态:InterruptedException会清除线程的中断标志,必须手动恢复,否则循环条件!Thread.currentThread().isInterrupted()会无法检测到中断,导致线程无法正常退出。
  • 精准释放:只有当标志位为true时才调用release,确保不会释放未持有的许可。

方法二:结合tryAcquire实现非阻塞式资源获取

如果你的场景允许非阻塞等待资源,可以用tryAcquire替代acquire,同样通过标志位控制释放逻辑:

public void run() {
    while (!Thread.currentThread().isInterrupted()) {
        boolean hasA = false;
        boolean hasB = false;
        try {
            // 尝试获取A,成功后再尝试获取B
            if (A.tryAcquire(2)) {
                hasA = true;
                if (B.tryAcquire(2)) {
                    hasB = true;
                    // 执行业务逻辑
                    doBusinessLogic();
                }
            }
            // 如果获取任意资源失败,直接进入finally释放已获取的部分
        } catch (Exception ex) {
            // 处理其他意外异常
            ex.printStackTrace();
        } finally {
            if (hasB) B.release(2);
            if (hasA) A.release(2);
        }
        
        // 避免线程空转,增加短暂休眠
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }
}

适用场景:

这种方式适合不需要无限等待资源的场景,tryAcquire会立即返回结果,不会让线程进入阻塞状态。同时同样遵循“仅释放已获取资源”的原则,避免Semaphore计数错误。

为什么你的原代码会出问题?

原代码中,无论是否成功获取B,finally块都会调用B.release(2)——如果线程在获取A之后、获取B之前被中断,B从未被acquire过,但release操作会直接增加Semaphore的许可数,导致后续其他线程可能获取到超过实际可用的资源,彻底打乱Semaphore的同步逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:33:17