三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

线程中断-interrupted方法

线程中断-interrupted方法

一、没有中断机制,代码会出什么问题?

假设 Java 没有interrupt(),你要怎么让一个正在运行的线程停下来?

方案 1:用一个volatile boolean标志位

class Worker implements Runnable { private volatile boolean running = true; public void stop() { running = false; } @Override public void run() { while (running) { // 做任务 } } }

问题在哪?

如果线程正在Thread.sleep(10000)(睡了 10 秒),你调用了stop(),把running改成false线程要睡满 10 秒才会醒来检查running。这 10 秒内它什么都干不了,也无法响应你的停止请求。

方案 2:用Thread.stop()Java 已废弃

这个方法能强制杀死线程,但问题更严重:

  • 线程可能在写文件、操作数据库、持有锁,强制杀死会导致资源泄漏、数据损坏、锁永远不释放

所以 Java 设计者面临一个矛盾:

  • 不能强制杀(太危险)

  • 不能光用标志位(线程阻塞时无法及时响应)

于是interrupt()被设计出来:它不是强制停止,而是"礼貌地通知线程:有人希望你停下来,你自己决定什么时候停"。


二、interrupt()的核心设计:协作式中断

interrupt()的设计哲学是:发通知,不强制

它只做三件事:

  1. 给目标线程设置一个"中断标志位"(true

  2. 如果目标线程正在sleep()/wait()/join()立刻唤醒它,并抛出InterruptedException

  3. 如果线程正在正常运行,它不会自动停止,需要你手动检查中断状态

为什么这样设计?因为,线程最清楚自己什么时候是"安全的停止点"。比如:

// 线程正在执行转账操作: // 1. 从 A 账户扣款 // 2. 向 B 账户加款 // 如果在这两步之间被强制中断,钱扣了但没加上,数据就不一致了。 // 所以线程自己决定:等这两步原子操作做完,再检查中断标志,安全退出。

三、代码实战:三种场景

场景 1:线程在正常运行,你调用interrupt()

public class InterruptDemo { public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { // 模拟一个长时间运行的任务 for (int i = 0; i < 100000; i++) { // isInterrupted() 检查:有人让我中断吗? if (Thread.currentThread().isInterrupted()) { System.out.println("收到中断信号,我在第 " + i + " 次循环时安全退出"); return; // 自己决定在这里停 } // 做一些计算... } }); worker.start(); Thread.sleep(100); // 主线程等 100ms worker.interrupt(); // 发送中断请求 } }

输出:

收到中断信号,我在第 xxx 次循环时安全退出

关键点:

  • interrupt()只是设了个标志

  • 线程通过isInterrupted()主动检查

  • 什么时候停,由线程自己决定(这就是协作式)


场景 2:线程在sleep(),你调用interrupt()

public class SleepInterruptDemo { public static void main(String[] args) { Thread sleeper = new Thread(() -> { try { System.out.println("我要睡 10 秒..."); Thread.sleep(10000); System.out.println("睡醒了"); // 这行不会执行 } catch (InterruptedException e) { System.out.println("sleep 期间被中断了!异常信息:" + e.getMessage()); // 注意:catch 之后,中断标志位会被清空(变成 false) System.out.println("catch 后中断标志:" + Thread.currentThread().isInterrupted()); } }); sleeper.start(); // 主线程等 500ms 后中断它 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } sleeper.interrupt(); } }

输出:

我要睡 10 秒... sleep 期间被中断了!异常信息:sleep interrupted catch 后中断标志:false

设计原因:

  • 如果线程在sleep(),它无法执行isInterrupted()检查

  • 所以 JVM 设计成:收到中断时立刻抛异常,强制线程醒来

  • 但抛异常后,中断标志会被清除(这是 Java 的设计约定,防止你重复处理)


场景 3:线程在wait(),你调用interrupt()

public class WaitInterruptDemo { private static final Object lock = new Object(); public static void main(String[] args) { Thread waiter = new Thread(() -> { synchronized (lock) { try { System.out.println("我要 wait 了..."); lock.wait(); // 释放锁,进入等待 System.out.println("被 notify 了"); // 不会执行 } catch (InterruptedException e) { System.out.println("wait 期间被中断了!"); // 同样,中断标志会被清除 } } }); waiter.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } waiter.interrupt(); // 中断 wait 中的线程 } }

输出:

我要 wait 了... wait 期间被中断了!

sleep()的区别:

  • sleep()不释放锁,抱着锁睡觉

  • wait()会释放锁,进等待队列

  • 但两者被中断时,都会立刻InterruptedException,并清除中断标志


四、一个容易踩的坑:中断标志被清除了怎么办?

看这段代码:

while (!Thread.currentThread().isInterrupted()) { try { doWork(); Thread.sleep(1000); // 假设这里被中断 } catch (InterruptedException e) { // 异常被吞掉,什么都不做 // 糟糕!中断标志已经被清除了! } } // 因为标志被清除,循环会继续执行,线程永远停不下来!

正确做法:catch里重新设置中断标志,或者干脆退出:

while (!Thread.currentThread().isInterrupted()) { try { doWork(); Thread.sleep(1000); } catch (InterruptedException e) { // 方案 A:重新设置标志,让上层处理 Thread.currentThread().interrupt(); // 方案 B:直接 break/return,在这里处理退出 break; } }

五、总结:一张图看懂逻辑

主线程调用 t.interrupt() │ ▼ ┌─────────────────┐ │ 设置 t 的中断标志位 = true │ └─────────────────┘ │ ├──► 如果 t 正在运行 ──► t 自己用 isInterrupted() 检查 │ │ │ ▼ │ 决定什么时候安全退出 │ ├──► 如果 t 正在 sleep()/wait()/join() │ ▼ 立刻唤醒,抛 InterruptedException │ ▼ 中断标志位被自动清除(变回 false) │ ▼ 在 catch 里:要么重新 interrupt(),要么 break/return

六、核心要点(记住这几条就够了)

方法作用注意
t.interrupt()给线程 t 发中断请求只是请求,不强制
Thread.interrupted()检查当前线程是否被中断,并清除标志静态方法,会清标志
t.isInterrupted()检查线程 t 是否被中断,不清除标志实例方法
sleep/wait/join被中断立刻抛InterruptedException抛完后标志位变false

设计的本质:interrupt()提供了一种"既能唤醒阻塞线程,又不强制破坏其执行逻辑"的安全机制。没有它,你要么无法及时停止阻塞线程,要么只能用危险的强制手段。

← 返回列表