Java 在调用 wait() 或者 sleep() 方法时,需要处理中断异常 InterruptedException。我们需要了解为什么会出现这个异常,以及如何避免出现这样的异常。
基础回顾
中断异常 InterruptedException 是在多线程条件下出现的,在 Java 中,应用程序从一个与main()方法相关联的线程(称为主线程)开始,然后这个主线程可以启动其他线程。
下面是线程的生命周期:
一旦我们创建了一个新线程,它就处于新建(NEW)状态。它一直保持这种状态,直到程序调用 start() 方法启动线程。
调用 start() 方法会将其置于就绪(RUNNABLE)状态。处于此状态的线程要么正在运行,要么已准备好运行。
当一个线程正在等待锁并试图访问被其他线程锁定的代码时,它会进入阻塞(BLOCKED)状态。
可以通过各种事件将线程置于暂停(WAITING)状态,例如调用wait()方法。在这种状态下,一个线程正在等待来自另一个线程的信号。
当线程完成执行或异常终止时,它将以死亡(TERMINATED)状态结束。线程可以被中断,当一个线程被中断时,它会抛出InterruptedException。
理解 InterruptedException
如果线程在等待、休眠或以其他方式被占用时被中断,则会引发InterruptedException。换句话说,一些代码在我们的线程上调用了 Interrupt()方法。
为什么会存在中断系统呢?中断方法允许开发者在程序运行过程中,暂时挂起任务。例如某些操作是IO密集型任务或者一个耗时的计算,中断后可以让出CPU资源。
InterruptedException通常由所有阻塞方法抛出,以便可以处理它并执行纠正操作。Java 中有几种抛出InterruptedException的方法,例如 Thread.sleep()、Thread.join()、Object.wait()方法,以及BlockingQueue的put()和take()方法。
中断方法
Thread 类中提供了一些关于中断的关键方法:
public void interrupt() { ... }
public boolean isInterrupted() { ... }
public static boolean interrupted() { ... }
interrupt() 方法可以用来中断线程。还有两个方法都可以用来判断线程是否被中断,使用时可能存在困惑。
isInterrupted 与 interrupted
从源码看,他们两者有一定关系。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);
- isInterrupted 与 interrupted 都返回布尔值
- 都是用来判断线程是否被中断
- 二者最终的值都由 native 的 isInterrupted 决定
- isInterrupted 为实例方法,用来检查调用它的 Thread
- interrupted 为静态方法,用来检查当前 Thread
- interrupted 会清除中断状态,连续调用两次该方法,无论第一次返回什么,第二次一定返回 false
- isInterrupted 不会清除中断状态,连续调用两次该方法,返回值一定相同
- interrupted 好像是在询问,之前是否调用过interrupt()
- isInterrupted 好像是在询问,调用它的线程当前是否被中断
处理 InterruptedException
线程间的调度依赖 JVM,而 JVM 的多线程需要操作系统底层来处理。因此,虽然我们能通过代码中断线程,但无法保证何时被中断,也不能保证一定能中断。
中断是给线程的指令,告诉它应该停止正在做的事情而去做其他事情。中断以后,我们还需要有程序能响应中断,避免出现死锁的情况。
throws
我们可以给需要处理中断的方法添加 throws 来向上抛出异常,让调用者确定如何处理该中断。
public static void stopHere() throws InterruptedException {
Thread.sleep(1000);
}
当然,这样需要确保最终是有方法处理该异常,否则在最后,程序执行到该处代码时,会直接将 InterruptedException 抛出而导致程序意外结束。
catch
处理异常的第二种方式就是捕获该异常并处理掉。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
一种处理方式为再次调用 interrupt(),以便调用堆栈中更高层级的代码可以看到发出了中断。