并发编程中,线程是一种重要的执行单元,能够提供更高效的程序运行。然而,在多线程编程中,我们经常面临着需要终止或停止线程的需求。Java 提供了线程中断机制,允许我们优雅地终止正在执行的线程。

1、什么是线程中断?

在线程上调用 interrupt()`方法会设置该线程的中断标志位为 true。通过检查这个标志位,线程可以自行选择是否响应中断请求并采取相应措施。 需要注意的是,interrupt() 方法并不会真正中断一个正在运行的线程,它只是向目标线程发出一个请求以终止其执行或改变其行为。

2、中断标识

在一个线程内部存在着名为interrupt flag的标识,如果一个线程被interrupt,那么它的flag将被设置,但是如果当前线程正在执行可中断方法被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除。

3、interrupt()方法

调用sleep或wait方法时,会使得当前线程进入阻塞状态,而调用当前线程的interrupt方法,就可以打断阻塞。打断一个线程并不等于该线程的生命周期结束,仅仅是打断了当前线程的阻塞状态。一旦线程在阻塞的情况下被打断,都会抛出一个称为InterruptedException的异常。

4、InterruptedException中断异常

wait方法和sleep方法结束运行前线程被中断了会抛出InterruptedException,并且清除中断状态。但是并不会终止当前线程的执行,当前线程可以选择忽略这个异常。

无论是设置interrupt status 还是抛出InterruptedException,它们都是给当前线程的建议,当前线程可以选择采纳或者不采纳,它们并不会影响当前线程的执行。

至于在收到这些中断的建议后,当前线程要怎么处理,也完全取决于当前线程。

5、判断线程是否被中断

判断某个线程是否已被发送过中断请求,请使用Thread.currentThread().isInterrupted()方法(因为它将线程中断标示位设置为true后,不会立刻清除中断标示位,即不会将中断标设置为false),而不要使用thread.interrupted()(该方法调用后会将中断标示位清除,即重新设置为false)方法来判断,下面是线程在循环中时的中断方式:

while(!Thread.currentThread().isInterrupted() ){ // do more work } 

6、线程对中断的反应

  1. RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度
  2. WAITING/TIMED_WAITING:线程在等待某个条件或超时
  3. BLOCKED:线程在等待锁,试图进入同步块
  4. NEW/TERMINATED:线程还未启动或已结束

7、代码示例

public class InterruptThreadTest { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + "正在检测心跳"); System.out.println("中断状态" + Thread.interrupted()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { // 被中断时 退出循环 e.printStackTrace(); break; } } } }, "检测心跳任务线程"); // 获取当前线程 t.start(); try { Thread.sleep(20_000); } catch (InterruptedException e) { e.printStackTrace(); } // 中断 t.interrupt(); System.out.println(Thread.currentThread().getName() + "线程任务运行结束"); } } 

8、正确处理线程中断

正确处理线程中断非常重要,以下是几点建议:

  • 1. 针对可阻塞操作:例如使用 `sleep()`, `wait()`, `join()` 等方法时,在捕获到 `InterruptedException` 异常后,请确保及时恢复中断状态,并决定是否继续执行或退出。
  • 2. 对于不可阻塞操作:例如计算密集型任务,在代码逻辑允许情况下,请定期检查并响应捕获到的异常或检查 `isInterrupted()` 标志位。
  • 3. 增加程序鲁棒性:设定恰当的超时时间和循环条件,并考虑合适的退出策略。

9、示例代码

以下是一个简单示例代码,展示了如何正确处理线程中断:

public class InterruptExample implements Runnable { public void run() { try { while (!Thread.currentThread().isInterrupted()) { // 执行任务逻辑 // ... } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复设置当前线程为已经中断状态 } } } 

10、结论

Java 的线程中断机制提供了一种优雅且安全地终止正在执行任务的方式。仔细学习和理解如何检测和响应中断以及正确处理其中段对于开发健壮性高且可靠性强的多线程序非常重要。在实际使用过程中,请遵循最佳实践并根据具体需求进行灵活合理地设计。