线程中断异常下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
相关产品推荐
相关产品推荐

