Thread.currentThread().interrupt() 线程中断原理详解
引言
在 Java 多线程编程中,线程中断是一个重要但容易被误解的概念。Thread.currentThread().interrupt()
是处理线程中断的标准方式,但很多开发者对其工作原理和最佳实践并不清楚。本文将深入探讨线程中断的机制、原理和正确的处理方式。
目录
线程中断的基本概念
什么是线程中断?
线程中断是 Java 提供的一种协作机制,用于通知线程应该停止当前的工作。与强制终止线程不同,中断是一种"建议"机制,线程可以选择响应中断或忽略它。
中断相关的核心方法
// 中断线程
public void interrupt()
// 检查线程是否被中断
public boolean isInterrupted()
// 检查当前线程是否被中断,并清除中断状态
public static boolean interrupted()
// 检查线程是否处于中断状态
public boolean isInterrupted()
中断标志位的机制
中断状态标志
每个线程都有一个布尔类型的中断状态标志(interrupt status),这个标志表示线程是否被中断:
public class ThreadInterruptDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程正在运行...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 当线程在 sleep 时被中断,会抛出 InterruptedException
// 并且中断状态会被清除
System.out.println("线程被中断,中断状态: " + Thread.currentThread().isInterrupted());
// 重新设置中断状态
Thread.currentThread().interrupt();
break;
}
}
System.out.println("线程结束,中断状态: " + Thread.currentThread().isInterrupted());
});
thread.start();
// 3秒后中断线程
try {
Thread.sleep(3000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
中断状态的变化
public class InterruptStatusDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println("初始中断状态: " + thread.isInterrupted()); // false
thread.interrupt();
System.out.println("调用 interrupt() 后: " + thread.isInterrupted()); // true
// interrupted() 会检查并清除中断状态
System.out.println("调用 interrupted(): " + Thread.interrupted()); // true
System.out.println("再次检查中断状态: " + thread.isInterrupted()); // false
// 重新设置中断状态
thread.interrupt();
System.out.println("重新设置中断状态: " + thread.isInterrupted()); // true
}
}
Thread.currentThread().interrupt() 的作用
核心作用
Thread.currentThread().interrupt()
的作用是重新设置当前线程的中断状态。这在处理 InterruptedException
时特别重要。
为什么需要重新设置中断状态?
当线程在以下阻塞方法中被中断时,会抛出 InterruptedException
并清除中断状态:
Thread.sleep()
Object.wait()
BlockingQueue.take()
CountDownLatch.await()
CyclicBarrier.await()
Semaphore.acquire()
Future.get()
public class InterruptedExceptionDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
// 线程进入睡眠状态
Thread.sleep(5000);
} catch (InterruptedException e) {
// 当线程在 sleep 时被中断,会抛出 InterruptedException
// 此时中断状态已经被清除
System.out.println("捕获到 InterruptedException");
System.out.println("中断状态: " + Thread.currentThread().isInterrupted()); // false
// 重新设置中断状态
Thread.currentThread().interrupt();
System.out.println("重新设置中断状态: " + Thread.currentThread().isInterrupted()); // true
}
});
thread.start();
// 立即中断线程
thread.interrupt();
}
}
线程中断的处理方式
1. 检查中断状态
public class InterruptCheckDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
System.out.println("执行任务...");
// 模拟工作
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// 处理中断
Thread.currentThread().interrupt();
break;
}
}
System.out.println("线程结束");
});
thread.start();
// 2秒后中断
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
2. 响应中断异常
public class InterruptResponseDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
// 可能抛出 InterruptedException 的操作
processWithInterrupt();
} catch (InterruptedException e) {
// 处理中断
System.out.println("线程被中断,进行清理工作...");
cleanup();
// 重新设置中断状态
Thread.currentThread().interrupt();
}
});
thread.start();
// 中断线程
thread.interrupt();
}
private static void processWithInterrupt() throws InterruptedException {
// 模拟可能被中断的操作
Thread.sleep(1000);
}
private static void cleanup() {
System.out.println("执行清理工作...");
}
}
3. 传播中断状态
public class InterruptPropagationDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
// 调用可能被中断的方法
doWork();
} catch (InterruptedException e) {
// 不处理中断,直接重新抛出
Thread.currentThread().interrupt();
}
});
thread.start();
thread.interrupt();
}
private static void doWork() throws InterruptedException {
// 模拟工作
Thread.sleep(1000);
}
}
常见的中断处理模式
模式1:立即响应中断
public class ImmediateInterruptResponse {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
doTask();
}
} catch (InterruptedException e) {
// 立即响应中断
System.out.println("收到中断信号,立即停止");
Thread.currentThread().interrupt();
}
});
thread.start();
thread.interrupt();
}
private static void doTask() throws InterruptedException {
// 模拟任务执行
Thread.sleep(100);
}
}
模式2:优雅关闭
public class GracefulShutdown {
private volatile boolean shutdown = false;
public void start() {
Thread worker = new Thread(() -> {
while (!shutdown && !Thread.currentThread().isInterrupted()) {
try {
// 执行任务
processTask();
} catch (InterruptedException e) {
// 收到中断信号,开始优雅关闭
System.out.println("开始优雅关闭...");
shutdown = true;
Thread.currentThread().interrupt();
}
}
// 执行清理工作
cleanup();
});
worker.start();
}
public void stop() {
shutdown = true;
}
private void processTask() throws InterruptedException {
// 模拟任务处理
Thread.sleep(100);
}
private void cleanup() {
System.out.println("执行清理工作...");
}
}
模式3:中断传播
public class InterruptPropagation {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
// 调用可能被中断的方法
methodA();
} catch (InterruptedException e) {
// 传播中断状态
Thread.currentThread().interrupt();
}
});
thread.start();
thread.interrupt();
}
private static void methodA() throws InterruptedException {
methodB();
}
private static void methodB() throws InterruptedException {
methodC();
}
private static void methodC() throws InterruptedException {
// 可能被中断的操作
Thread.sleep(1000);
}
}
中断处理的最佳实践
1. 正确处理 InterruptedException
public class BestPracticeDemo {
// ❌ 错误做法:忽略中断
public void wrongWay() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 什么都不做,这是错误的!
}
}
// ✅ 正确做法1:重新设置中断状态
public void correctWay1() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// ✅ 正确做法2:抛出 InterruptedException
public void correctWay2() throws InterruptedException {
Thread.sleep(1000);
}
// ✅ 正确做法3:进行清理工作后重新设置中断状态
public void correctWay3() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 执行清理工作
cleanup();
// 重新设置中断状态
Thread.currentThread().interrupt();
}
}
private void cleanup() {
System.out.println("执行清理工作...");
}
}
2. 使用中断标志进行循环控制
public class InterruptFlagDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
// 使用中断标志控制循环
while (!Thread.currentThread().isInterrupted()) {
try {
// 执行任务
doWork();
} catch (InterruptedException e) {
// 重新设置中断状态并退出循环
Thread.currentThread().interrupt();
break;
}
}
System.out.println("线程正常结束");
});
thread.start();
// 3秒后中断
try {
Thread.sleep(3000);
thread.interrupt();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private static void doWork() throws InterruptedException {
// 模拟工作
Thread.sleep(500);
System.out.println("执行任务...");
}
}
3. 在 ExecutorService 中处理中断
public class ExecutorServiceInterruptDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> future = executor.submit(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
processTask();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 5秒后取消任务
try {
Thread.sleep(5000);
future.cancel(true); // 中断任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
executor.shutdown();
}
private static void processTask() throws InterruptedException {
Thread.sleep(1000);
System.out.println("处理任务...");
}
}
实际应用场景
1. 定时任务的中断处理
public class ScheduledTaskDemo {
private volatile boolean running = true;
public void startScheduledTask() {
Thread taskThread = new Thread(() -> {
while (running && !Thread.currentThread().isInterrupted()) {
try {
// 执行定时任务
executeScheduledTask();
// 等待下一次执行
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("定时任务被中断");
Thread.currentThread().interrupt();
break;
}
}
});
taskThread.start();
}
public void stopScheduledTask() {
running = false;
}
private void executeScheduledTask() {
System.out.println("执行定时任务: " + System.currentTimeMillis());
}
}
2. 网络请求的超时处理
public class NetworkRequestDemo {
public String makeRequestWithTimeout(String url, long timeout) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
Future<String> future = executor.submit(() -> {
// 模拟网络请求
Thread.sleep(2000);
return "Response from " + url;
});
return future.get(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
throw new RuntimeException("请求超时");
} finally {
executor.shutdown();
}
}
}
3. 资源清理的中断处理
public class ResourceCleanupDemo {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
private volatile boolean shutdown = false;
public void startConsumer() {
Thread consumer = new Thread(() -> {
try {
while (!shutdown && !Thread.currentThread().isInterrupted()) {
// 从队列中获取数据
String data = queue.take();
processData(data);
}
} catch (InterruptedException e) {
System.out.println("消费者被中断,开始清理...");
cleanup();
Thread.currentThread().interrupt();
}
});
consumer.start();
}
public void shutdown() {
shutdown = true;
}
private void processData(String data) {
System.out.println("处理数据: " + data);
}
private void cleanup() {
System.out.println("清理资源...");
queue.clear();
}
}
总结
关键要点
- 线程中断是协作机制:不是强制终止,而是通知线程应该停止工作
- 中断状态会被清除:某些阻塞操作会清除中断状态,需要重新设置
- Thread.currentThread().interrupt() 的作用:重新设置当前线程的中断状态
- 正确处理 InterruptedException:不要忽略,要重新设置中断状态或抛出异常
最佳实践总结
- 始终检查中断状态:在长时间运行的循环中检查
Thread.currentThread().isInterrupted()
- 正确处理 InterruptedException:捕获后要重新设置中断状态
- 传播中断状态:在方法调用链中正确传播中断状态
- 进行清理工作:在响应中断时执行必要的清理操作
- 使用中断标志控制循环:而不是使用 volatile boolean 标志
常见错误
- 忽略 InterruptedException:不处理或空 catch 块
- 不重新设置中断状态:在捕获 InterruptedException 后不调用
Thread.currentThread().interrupt()
- 使用 stop() 方法:已被废弃,不要使用
- 混合使用中断和 volatile 标志:应该统一使用中断机制
通过正确理解和处理线程中断,可以编写出更加健壮和响应性更好的多线程应用程序。Thread.currentThread().interrupt()
是处理中断状态的标准方式,掌握其原理和最佳实践对于 Java 多线程编程至关重要。