Condition 的异常处理中断机制
Condition 的异常处理中断机制是 Java 并发编程中一个极为重要但容易被忽视的细节。它涉及到线程在等待(await())过程中,如何正确响应中断,以及开发者应该如何优雅处理线程中断、资源清理和状态恢复。
我们将从多个维度详解:
一、Condition 的中断处理方式概览
Java 中 Condition 接口提供了多个 await() 方法,其中:
| 方法 | 是否响应中断 | 可设置超时 | 可处理虚假唤醒 |
|---|---|---|---|
await() | 响应中断 | ❌ | ✅ |
await(long time, TimeUnit) | 响应中断 | ✅ | ✅ |
awaitUninterruptibly() | 不响应中断 | ❌ | ✅ |
二、被中断时会发生什么?
await() 方法中断行为流程:
当线程在 await() 等待期间被其他线程调用了 interrupt() 方法:
- 抛出
InterruptedException异常 - 线程从 Condition 等待队列移除
- 线程中断标志位被清除(清除!)
示例:
lock.lock();
try {
condition.await(); // 此处如果被中断,会立即抛出 InterruptedException
// 中断后以下代码不会执行
} catch (InterruptedException e) {
System.out.println("被中断啦!");
// 可以选择恢复中断状态
Thread.currentThread().interrupt(); // ✅ 可选恢复
} finally {
lock.unlock();
}
三、虚假唤醒与中断的结合处理(推荐模式)
条件等待推荐模式为 条件+循环:
lock.lock();
try {
while (!某个条件成立) {
condition.await(); // 可能被中断或虚假唤醒
}
// 条件成立,执行逻辑
} catch (InterruptedException e) {
// 中断处理,记录日志 / 退出线程 / 恢复中断状态
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
while是防止虚假唤醒(spurious wakeup)的标准做法。
四、源码层面理解中断检测流程
ConditionObject.await() 内部关键代码:
if (Thread.interrupted()) {
throw new InterruptedException();
}
之后线程被 park() 挂起,如果此时被中断,将:
- 从条件队列移出
- 转入 AQS 同步队列(中断状态优先执行)
- 或在 AQS 内部判断为中断,提前异常返回
五、恢复中断状态的建议
Java 中断的设计是协作式的,如果你捕获了 InterruptedException,但决定不立即终止线程,建议恢复中断状态:
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
}
否则,调用方或线程控制器将无法再通过 Thread.isInterrupted() 判断中断状态。
六、为什么不要用 awaitUninterruptibly() 随便屏蔽中断?
虽然它不会抛异常、也不会响应中断,但:
- 容易导致线程永久挂起或死锁;
- 不利于任务取消/线程池回收;
- 通常仅在特定场景下(如 finally 中强制等待资源)才推荐使用。
七、Condition 中断处理 vs Object.wait()
| 特性 | Condition.await() | Object.wait() |
|---|---|---|
| 抛异常 | 抛出 InterruptedException | 抛出 InterruptedException |
| 中断状态恢复 | 自动清除 | 自动清除 |
| 推荐恢复中断状态 | 推荐 | 推荐 |
| 更丰富的等待控制方法 | 支持限时、非中断等 | 较少 |
八、官方文档与权威资料
- Java Doc – java.util.concurrent.locks.Condition
- 《Java 并发编程实战》 — 第 14 章
- 《Effective Java》第三版 Item 44: 优先使用标准机制处理中断
- OpenJDK AQS 实现源码
总结口诀
await能响应中断,异常抛出状态清;
捕获别忘重置它,协作终止才文明。while配套做等待,虚假唤醒靠它行。
更多详细内容请关注其他相关文章!