3. 线程中断机制的正确使用
中断机制的核心:标志位 + 阻塞唤醒
Java 的线程中断不是强制杀死线程,而是协作式的:interrupt() 设置目标线程的中断标志位;如果目标线程正阻塞在 sleep()、wait()、join()、BlockingQueue.take() 等可中断方法上,JVM 会抛出 InterruptedException 并清除中断标志位。理解“标志位是否被清除”是正确使用中断的关键。
可中断阻塞任务的完整实现
下面实现一个可中断的阻塞任务:工作线程循环从阻塞队列取任务,主线程在 2 秒后调用 interrupt()。通过日志观察 isInterrupted() 在阻塞前后的变化。
import java.util.concurrent.*;
public class InterruptDemo {
public static void main(String[] args) throws Exception {
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
Thread worker = new Thread(() -> {
System.out.println("[Worker] 启动,准备阻塞在 take()");
try {
while (!Thread.currentThread().isInterrupted()) {
String task = queue.take(); // 可中断阻塞点
System.out.println("[Worker] 处理: " + task);
}
} catch (InterruptedException e) {
System.out.println("[Worker] 捕获 InterruptedException");
System.out.println("[Worker] 标志位 = "
+ Thread.currentThread().isInterrupted()); // false
Thread.currentThread().interrupt(); // 恢复标志位
System.out.println("[Worker] 恢复后标志位 = "
+ Thread.currentThread().isInterrupted()); // true
}
System.out.println("[Worker] 退出");
}, "worker");
worker.start();
Thread.sleep(2000);
System.out.println("[Main] 调用 interrupt()");
worker.interrupt();
worker.join();
System.out.println("[Main] 结束");
}
}
关键行为验证
| 场景 | 调用 interrupt() 后 | isInterrupted() | 异常 |
|---|---|---|---|
| 阻塞在 take()/sleep() | JVM 唤醒线程 | false(被清除) | InterruptedException |
| 正在执行计算循环 | 仅设置标志位 | true | 无 |
| 已终止的线程 | 无效果 | false | 无 |
常见陷阱:在
catch (InterruptedException) 中吞掉异常而不恢复标志位,会导致上层调用者无法感知中断请求,线程可能继续运行。正确做法是:要么重新抛出,要么调用 Thread.currentThread().interrupt() 恢复标志位后退出。
验证要点:运行程序后,观察
[Worker] 标志位 = false 这一行——它证明 InterruptedException 抛出时 JVM 已清除中断标志。随后手动恢复标志位,isInterrupted() 才重新返回 true。