线程中断(Interruption)机制
一
在Java中,线程的中断(Interruption)是一种协作机制,用于通知线程需要停止当前任务或操作。中断不会强制终止线程,而是通过线程间的协作让目标线程自行决定何时以及如何退出。以下是线程中断的作用和使用场景:
1-1. 中断的作用
- 优雅终止线程:避免直接调用
stop()方法(已过时,不安全),通过协作机制让线程有序退出。 - 响应外部请求:例如,在用户点击“取消”按钮时,中断后台任务。
- 处理阻塞操作:中断可唤醒处于阻塞状态的线程(如
wait()、sleep()、join()),并抛出InterruptedException异常。 - 资源清理:在中断响应中释放资源、保存状态,避免资源泄漏。
1-2. 中断的实现方式
(1) 设置中断标志
调用线程对象的interrupt()方法,将线程的中断标志位设为true。
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务...
}
});
thread.start();
thread.interrupt(); // 设置中断标志
(2) 检查中断状态
Thread.currentThread().isInterrupted():检查当前线程是否被中断。Thread.interrupted():检查中断状态并清除标志(返回true后标志会被重置为false)。
(3) 处理阻塞操作
当线程执行阻塞方法(如sleep()、wait()、join())时,若被中断,会抛出InterruptedException异常。此时需在异常处理中响应中断:
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000); // 阻塞1秒
} catch (InterruptedException e) {
// 1. 处理中断:清理资源、退出循环
System.out.println("线程被中断,准备退出");
Thread.currentThread().interrupt(); // 重新设置中断标志(可选)
break;
}
}
}
1-3. 注意事项
- 协作机制:中断需要目标线程主动检查中断标志或处理异常,否则无法生效。
- 不可中断操作:某些操作(如
I/O读取)不会响应中断,需结合其他方式(如关闭流)终止。 - 避免忽略异常:捕获
InterruptedException后应妥善处理,否则线程可能无法终止。
1-4. 示例场景
假设一个需要长时间计算的线程,用户可通过中断提前终止任务:
public class Calculator implements Runnable {
private volatile boolean running = true;
@Override
public void run() {
while (running && !Thread.currentThread().isInterrupted()) {
// 执行计算...
if (Thread.currentThread().isInterrupted()) {
System.out.println("任务被中断,结果已保存");
break;
}
}
}
public void stop() {
running = false;
Thread.currentThread().interrupt(); // 设置中断标志
}
}
1-5. 总结
线程中断是Java中实现线程间协作的重要机制,适用于需要优雅终止任务的场景。通过中断标志和异常处理,线程可以安全地响应外部请求,避免强制终止带来的风险。
二
线程中断是一种协作机制,interrupt() 方法仅设置中断标志(interrupt flag),具体如何处理中断需要线程自身根据标志位或异常进行响应。以下是关于中断标志的详细说明和最佳实践:
2-1. 中断标志的作用
- 标志位:每个线程内部有一个布尔型的中断标志(默认
false)。 - 设置标志:调用
thread.interrupt()会将标志设为true。 - 检查标志:线程通过
isInterrupted()或Thread.interrupted()方法检查标志。
2-2. 中断标志的检查方式
(1) isInterrupted()
- 非静态方法:返回当前线程的中断标志状态(不清除标志)。
- 示例:
(2) Thread.interrupted()
- 静态方法:返回当前线程的中断标志状态,并清除标志(调用后标志重置为
false)。 - 示例:
2-3. 阻塞方法中的中断处理
当线程执行阻塞方法(如 sleep()、wait()、join())时,若被中断,会抛出 InterruptedException 异常,此时需:
- 处理异常:释放资源、保存状态。
- 重新设置中断标志(可选):若需要上层代码感知中断,可再次调用
interrupt()。
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000); // 阻塞1秒
} catch (InterruptedException e) {
// 处理中断:
System.out.println("线程被中断,清理资源...");
Thread.currentThread().interrupt(); // 重新设置中断标志
break; // 退出循环
}
}
}
2-4. 非阻塞代码中的中断处理
在非阻塞代码中,需主动检查中断标志:
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// 执行非阻塞任务...
if (someCondition) {
// 提前响应中断
System.out.println("任务提前完成,响应中断");
Thread.currentThread().interrupt();
break;
}
}
}
2-5. 最佳实践
- 优先使用
isInterrupted():避免清除标志,确保上层代码可感知中断。 - 处理
InterruptedException:在阻塞方法中捕获异常后,需明确是否继续中断。 - 清理资源:在中断响应中释放锁、关闭文件/网络连接等。
- 不可中断操作:对无法响应中断的操作(如
I/O读取),需结合其他方式终止(如关闭流)。
2-6. 示例代码:中断响应流程
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("线程被中断,准备退出");
break;
}
try {
Thread.sleep(1000); // 模拟阻塞任务
} catch (InterruptedException e) {
System.out.println("睡眠时被中断,重新设置标志");
Thread.currentThread().interrupt(); // 重新设置中断标志
}
}
});
thread.start();
Thread.sleep(3000); // 主线程等待3秒
thread.interrupt(); // 设置中断标志
}
}
输出:
睡眠时被中断,重新设置标志 线程被中断,准备退出
2-7总结
- 中断标志是线程间协作的“信号”,线程需主动检查并响应。
InterruptedException是中断协作的核心,需在异常处理中妥善处理。- 不可依赖
stop():强制终止线程可能导致数据不一致或资源泄漏。
三
线程中断机制本质上是一种协作机制,interrupt() 方法只是将线程的中断标志位设置为 true,若程序中没有代码去检查这个中断标志,或者没有对因中断抛出的 InterruptedException 进行处理,那么中断操作就无法产生预期的效果,也就相当于没有意义。下面从不同方面详细解释:
3-1. 非阻塞状态下的中断标志检查
当线程处于非阻塞状态时,它不会因为外部中断而自动停止执行,需要代码主动去检查中断标志。例如:
public class InterruptWithoutCheckExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true) {
// 模拟线程执行任务
System.out.println("线程正在执行任务...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
在上述代码中,虽然调用了 thread.interrupt() 方法设置了中断标志,但线程的 run() 方法中没有检查中断标志的逻辑,所以线程会继续执行,中断操作没有起到让线程停止的作用。
若要让中断操作生效,需要添加检查中断标志的代码:
public class InterruptWithCheckExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 模拟线程执行任务
System.out.println("线程正在执行任务...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// 重新设置中断标志
Thread.currentThread().interrupt();
}
}
System.out.println("线程因中断而停止执行。");
});
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
在这个改进后的代码中,线程会不断检查中断标志,当调用 thread.interrupt() 后,中断标志被设置为 true,线程会退出循环并停止执行。
3-2. 阻塞状态下的异常处理
当线程处于阻塞状态(如调用 sleep()、wait()、join() 等方法)时,若被中断,会抛出 InterruptedException 异常。如果程序没有捕获并处理这个异常,中断操作同样无法达到预期效果。例如:
public class InterruptWithoutExceptionHandlingExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
// 线程进入阻塞状态
Thread.sleep(10000);
} catch (InterruptedException e) {
// 这里没有对中断进行处理
}
System.out.println("线程继续执行。");
});
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
在这个例子中,虽然线程在 sleep() 时被中断并抛出了 InterruptedException 异常,但异常处理代码中没有对中断进行任何处理,线程只是简单地继续执行后续代码,中断操作没有实现让线程停止的目的。
正确的做法是在异常处理中进行相应的处理,比如退出线程:
public class InterruptWithExceptionHandlingExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
// 线程进入阻塞状态
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("线程被中断,准备退出。");
return;
}
System.out.println("线程继续执行。");
});
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
在这个改进后的代码中,当线程在 sleep() 时被中断并抛出 InterruptedException 异常,会在异常处理中输出提示信息并退出线程,中断操作达到了预期的效果。
综上所述,程序中对中断标志的检查和对 InterruptedException 的处理是让线程中断操作生效的关键。
JUC 是 Java.util.concurrent 包的简称,它是 Java 5 引入的一个用于处理并发编程的工具包,为 Java 开发者提供了一系列用于高效处理并发任务的类和接口,极大地简化了多线程编程的复杂性。
