在多线程编程中,线程中断是一种常见的控制线程执行流的机制,能够在一定程度上避免程序中线程因超时、死锁等原因而阻塞、浪费系统资源或造成程序卡死的问题。然而,直接停止线程的操作(如 Thread.stop()
或 Thread.suspend()
)是非常危险的,因为它们会带来不可预测的严重后果,比如线程持有的锁永远不会释放,导致其他线程永远无法获得锁而进入死锁状态。因此,Java 提供了一个较为优雅且安全的方式来中断线程,这就是通过使用中断信号来实现线程的安全终止。
Java 中的线程中断机制基于 Thread.interrupt()
方法。通过调用该方法,线程的中断标志位会被设置为 true
,从而通知线程可以提前退出或者处理一些中断逻辑。线程收到中断信号后,并不会立刻停止执行,而是需要在执行过程中主动检查中断标志,或者在调用一些会抛出中断异常的阻塞方法时做出响应。
Thread.interrupt()
后,线程的中断标志被设置为 true
,但如果线程并没有在阻塞状态中,它不会自动停止,只是改变了中断状态,线程可以通过 Thread.isInterrupted()
来查询中断状态。Thread.sleep()
、Object.wait()
、Thread.join()
等),线程在阻塞时如果收到中断信号,会抛出 InterruptedException
异常,并且将线程的中断标志重置为 false
。开发者可以在捕获该异常后,执行必要的中断处理逻辑。Thread.interrupted()
会返回并清除当前线程的中断标志,而 Thread.isInterrupted()
不会清除中断标志。如果需要检查当前线程的中断状态,可以使用 Thread.isInterrupted()
,而如果希望清除中断标志,可以使用 Thread.interrupted()
。对于非阻塞线程,在执行过程中会不断检查是否收到中断信号,通常这种方式适用于需要执行一系列工作或任务的线程。下面是一个非阻塞线程的例子,在其中我们通过 interrupt()
方法来中断线程。
@SneakyThrows
public static void main(String[] args) {
nonblockThread();
}
private static void nonblockThread() throws InterruptedException {
Thread thread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
while (true) {
System.out.println(currentThread + "运行中....." + currentThread.isInterrupted());
}
});
thread.start();
Thread.sleep(1000); // 主线程等待一秒
thread.interrupt(); // 中断线程
}
输出:
Thread[Thread-0,5,main]运行中.....false
Thread[Thread-0,5,main]运行中.....false
Thread[Thread-0,5,main]运行中.....false
...
Thread[Thread-0,5,main]运行中.....true
Thread[Thread-0,5,main]运行中.....true
Thread[Thread-0,5,main]运行中.....true
在上面的例子中,线程一开始正常运行,直到被中断。调用 interrupt()
方法后,线程的中断标志位被设置为 true
,使得 isInterrupted()
返回 true
。但是线程并没有自动停止,而是继续运行。
当线程处于阻塞状态时(例如调用 Thread.sleep()
、Object.wait()
或 Thread.join()
),线程会因收到中断信号而抛出 InterruptedException
异常,进而结束阻塞状态。
@SneakyThrows
public static void main(String[] args) {
blockThread();
}
private static void blockThread() throws InterruptedException {
Thread thread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread + "运行中.....");
try {
Thread.sleep(10 * 1000); // 让线程休眠10秒
System.out.println(currentThread + "任务结束");
} catch (InterruptedException e) {
e.printStackTrace();
boolean interrupted = currentThread.isInterrupted(); // 检查中断状态
System.out.println("interrupted = " + interrupted);
}
});
thread.start();
Thread.sleep(1000); // 主线程等待一秒
thread.interrupt(); // 中断线程
}
输出:
Thread[Thread-0,5,main]运行中.....
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.lxw.robot.tmp.ThreadTest.lambda$blockThread$1(ThreadTest.java:27)
at java.lang.Thread.run(Thread.java:748)
interrupted = false
在上面的代码中,线程调用了 Thread.sleep(10000)
来让自己休眠10秒,但在1秒后,主线程调用了 thread.interrupt()
来中断它。结果,阻塞线程抛出了 InterruptedException
异常并提前退出了 sleep
方法。
在捕获 InterruptedException
异常后,线程的中断标志会被重置为 false
。如果你希望线程继续处理其他的中断操作,可以显式地调用 Thread.interrupted()
来清除中断标志。
@SneakyThrows
public static void main(String[] args) {
blockThreadWithFlagReset();
}
private static void blockThreadWithFlagReset() throws InterruptedException {
Thread thread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread + "运行中.....");
try {
Thread.sleep(10 * 1000);
System.out.println(currentThread + "任务结束");
} catch (InterruptedException e) {
e.printStackTrace();
boolean interrupted = currentThread.isInterrupted();
System.out.println("interrupted = " + interrupted);
// 显式清除中断状态
Thread.interrupted();
}
});
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
在这里,调用 Thread.interrupted()
会清除当前线程的中断标志。在 InterruptedException
异常捕获后,调用 Thread.interrupted()
使得线程恢复为未中断状态。如果你想保持线程的中断状态不变,可以避免调用 Thread.interrupted()
。
线程中断是一种优雅且安全的控制线程的机制。与直接终止线程相比,线程中断允许线程自己根据情况做出响应。中断线程时,特别是在阻塞操作(如 sleep
或 wait
)中,能够提前退出并处理一些清理工作。在实际开发中,采用中断机制而非直接强制停止线程,可以避免许多潜在的问题,例如死锁、资源泄漏等。
Thread.isInterrupted()
检查线程的中断状态。InterruptedException
异常来响应中断信号。Thread.interrupted()
会清除当前线程的中断标志,而 Thread.isInterrupted()
则不会清除。通过合理的使用等待/通知机制和线程中断,可以在不强制终止线程的情况下,优雅地控制线程的生命周期和行为。