Java 中断不是强制终止线程,而是协作式取消信号。任务代码必须决定在哪里检查信号、怎样清理资源,以及是否向上层传播。
本文先运行一个可取消工作线程,再解释 wait、notify 和 join 的边界。
Table of contents
Open Table of contents
运行中断实验
下载中断协议样例下载, 在并发实验室运行:
mvn package
java -cp target/classes dev.elaine.concurrency.InterruptionSample
输出应包含:
收到中断,开始清理
工作线程已结束=true
interrupt() 只设置中断状态;线程在 sleep、wait、join 等可中断等待中会收到 InterruptedException。这些方法抛出
异常时会清除中断状态,因此示例在完成本层清理前调用 Thread.currentThread().interrupt() 恢复信号。
看懂六种线程状态
Thread.State 用六个枚举描述 Java 级状态:
| 状态 | 含义 | 常见来源 |
|---|---|---|
NEW | 尚未启动 | 刚创建的线程 |
RUNNABLE | 可运行或正在执行 | 计算、部分 I/O |
BLOCKED | 等待进入监视器 | 竞争 synchronized |
WAITING | 无限期等待 | wait()、join()、park() |
TIMED_WAITING | 有期限等待 | sleep()、带超时的等待 |
TERMINATED | 已结束 | run() 返回或抛出未捕获异常 |
RUNNABLE 不等于持续占用 CPU。线程转储中的 Java 状态需要结合多次采样、系统 CPU 和调用栈判断。
wait 与 notify 必须保护条件
wait() 会原子地释放当前对象监视器并等待;返回前会重新获得监视器。它可能被中断,也允许伪唤醒,所以必须在
while 中重新检查业务条件:
synchronized (queue) {
while (queue.isEmpty()) {
queue.wait();
}
return queue.removeFirst();
}
修改条件的一方也要持有同一监视器,再调用 notifyAll()。优先使用 BlockingQueue、CountDownLatch 等高级工具;只有
实现底层同步结构或维护旧代码时才直接使用等待集。
join 等待的是线程结束
worker.join() 让当前线程等待 worker 终止。工作线程终止前的操作 happens-before join() 成功返回后的操作,因此
调用方能看到工作线程最终写入。但生产等待应有超时或上层取消策略,避免一个失联任务永久卡住关闭流程。
中断处理规则
- 能结束当前任务时,清理资源后返回或抛出取消异常。
- 不能在当前层处理时,继续抛出
InterruptedException。 - 被无中断声明的接口限制时,恢复中断状态,让上层仍能发现信号。
- 不要写空的
catch (InterruptedException ignored) {}。 - 阻塞调用必须纳入整体截止时间,不能每层重新获得完整超时。
下一步
阅读JMM、Happens-Before 与三类并发性质,理解中断标志和 任务结果为何能跨线程可见。