3. 线程中断机制的正确使用

线程中断机制的正确使用 学习卡片

中断机制的核心:标志位 + 阻塞唤醒

Java 的线程中断不是强制杀死线程,而是协作式的:interrupt() 设置目标线程的中断标志位;如果目标线程正阻塞在 sleep()、wait()、join()、BlockingQueue.take() 等可中断方法上,JVM 会抛出 InterruptedException 并清除中断标志位。理解“标志位是否被清除”是正确使用中断的关键。

主线程 (Main) 调用 t.interrupt() 工作线程 (Worker) 阻塞在 sleep/take 中 中断标志位 interrupted = true interrupt() 线程是否阻塞? sleep / wait / take 抛出 InterruptedException 标志位被 JVM 清除 → false 需在 catch 中恢复或退出 是 标志位保持 true isInterrupted() 返回 true 循环中主动检查并退出 否 两种场景下的正确响应方式 阻塞场景(sleep / take) try { Thread.sleep(10000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复标志 return; // 退出任务 非阻塞场景(循环计算) while (!Thread.currentThread() .isInterrupted()) { // 执行任务片段 }

可中断阻塞任务的完整实现

下面实现一个可中断的阻塞任务:工作线程循环从阻塞队列取任务,主线程在 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。