如果没有人调用 interrupt(),是否可以忽略 InterruptedException?

Is it OK to ignore InterruptedException if nobody calls interrupt()?

如果我创建自己的线程(即不是线程池)并在某处调用 sleep 或任何其他可中断方法,是否可以忽略 InterruptedException 如果我不知道其他人在代码正在线程 上执行中断。

换句话说,如果线程应该与 JVM 一样长,这意味着线程不可中断,假设 InterruptedException 将 永远不会 被调用是否安全因此可以吞下异常?

如果您的 Projekt 变得越来越复杂,就很难说绝对没​​有人会调用中断方法。如果某天有人调用此方法并发生 InterruptedException,如果您至少没有在某处记录它,那么调试它会变得非常困难。

不容忽视。它应该被抛回给调用者,或者您应该重新断言线程的中断状态,因为在 InterruptedException 被抛出时 staid 状态被清除:

} catch (InterruptedException e) {
    // Restores the interrupted flag status.
    Thread.currentThread().interrupt();
}

除非你在捕获后立即退出线程。

Here 是一篇关于处理 InterruptedException 的文章。

例子

InterruptedException 仅从阻塞方法(如 Thread.sleep(...))中抛出,但绝不会从普通代码行中抛出, 由程序员决定线程如何响应中断。

例如,有一个项目只是对其进行了记录,例如:

public class MyTask {

// ...

/**
 * Waits for result to be use ready.
 * <br>
 * <br>
 * <b>Warning:</b> instead of throwing {@link InterruptedException},
 * returns {@code null}, and if that is undesired, do something like:
 *
 * <pre>{@code
 * MyResult result = myTask.await(...);
 * if (Thread.currentThread().isInterrupted()) {
 *     throw new InterruptedException();
 * }
 * }</pre>
 *
 * @param timeout Duration to wait at most.
 * @param unit    Type of duration.
 */
public MyResult await(long timeout, TimeUnit unit) {
    try {
        return /* ... */;
    } catch (InterruptedException e) {
        // Restores the interrupted flag status.
        Thread.currentThread().interrupt();
    }
    return null;
}

}

Note that above doc-block's code does not do if (Thread.interrupted()) {, because that would clear the interrupt flag on the thread (which is a bad pattern).

忽略已检查的异常永远不会被认为是安全的。
目前似乎对你来说没问题,但如果任何其他程序员使用你的code/API,他们应该期待标准行为:

  • 也就是说,线程对中断调用“做出反应”,但是线程文档描述的任何“反应”。

  • 我的意思是,由线程的开发人员来决定和记录线程如何处理中断,Java没有预定义规则,但您应该记录您的线程的“反应”(使用文档注释)。

  • 例如,InterruptedException 仅从阻塞方法(如 Thread.sleep(...))中抛出,但 从不从 的正常行中抛出代码(意味着默认情况下会简单地忽略中断),它允许开发人员选择(和记录)线程的反应,例如:

    • 也许中止,方法是取消正在进行的任务。

    Which takes time for development, if not already supported.

    • 也许推迟(或软忽略),方法是在 catch-block 中调用 Thread.currentThread().interrupt()

    Which simulates interrupt happening while normal code was running.

    • 可能 崩溃 ,方法是抛出 java.lang.Error(或子 class),并将 InterruptedException-实例作为 [=16] =].

    Which unless documented, is least desired or expected.

解释延期;一个空的 catch 块对于 InterruptedException 是危险的,因为 JVM 删除了中断标志,并且绝对应该在 catch 块中再次设置它,例如:

try {
    Thread.sleep(1000);
} catch (InterruptedException ignored) {
    Thread.currentThread().interrupt();
}

在我看来,这是 InterruptedExceptions 的最小捕获实现。在循环中检查 isInterrupted 标志也没什么坏处。
与您未来的程序员自己在一两天内搜索意外线程行为的麻烦相比,这是很小的开销,因为您的项目可能有所增长。

如果您觉得代码的可读性受到那些 catch 实现的影响,您可以实现自己的 safeSleep 实用方法,它会处理 Exception 并正确设置标志。

另一方面,InterruptedException 不会在硬件故障时由 JVM 本身抛出,它只是用户指示 Exception . 因此,如果您不传播您的 Threads 引用,将不会有任何其他 Threads 能够在其上调用 Thread.interrupt() 就是这样 技术上 但是你不应该低估人为因素和你的程序的发展。
编辑: 正如 ruakh 指出的那样,实际上有一种方法可以获取 Threads 参考,从而安排 [=26] =] 打电话。这样,热中的开发人员甚至不必查看实现不间断 Thread 的 class。在我看来,这是实现正确异常处理的另一个原因。
另一件事:如果您不抛出 Exception,则在 INFO 以外的级别记录此类事件可能是一个不错的选择。

与其吞下它,如果你确信它永远不会发生,你可以崩溃而不是忽略它。例如,抛出一个 Error(或 RuntimeException):

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    throw new Error(e);
}

来自the Javadocs for Error

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.

如果您认为值得假设某事永远不会发生,那么如果它真的发生了,那就是一种“异常情况”,谁知道呢,可能只是一个“严重问题”。

另一种看待这个问题的方法是,如果将来有人 确实 打断了你的方法,他们是否有可能希望它继续 运行 作为如果什么都没发生?我猜不会。

推迟

另一种选择是推迟,例如:

try {
    Thread.sleep(1000);
} catch (InterruptedException ignored) {
    Thread.currentThread().interrupt();
}

// ... do some work ...

if (Thread.currentThread().isInterrupted()) {
    // Handle if possible, and was expected,
    // else, throw new `InterruptedException`.
}

Note that above example-code does not do "if (Thread.interrupted()) {", because that would clear the interrupt flag of the thread (which is a bad pattern).

如果您认为异常永远不应该发生,那么您永远不应该吞下它。可以决定不添加代码来处理从未发生但不应发生的情况静默

您至少应该做的是:

catch(InterruptedException ex) {
  throw new AssertionError(ex);
}

这可以确保无论何时您关于这永远不会发生的断言是错误的,您都会注意到。此模式也适用于其他意外异常,例如IOException 当你知道目标 OutputStreamByteArrayOutputStream 或者 FormatterAppendable 应该是 StringBuilder,等等


顺便说一下,有一个 Thread.sleep 的替代方案根本不需要处理 InterruptedException

LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(timeInMillis));

当线程被中断时,此方法会更早 return 并保留 Thread 的中断状态,因此它已经为您的代码被某人使用的情况提供了正确的处理其他 使用中断的人(假设您的代码的调用者随后检查了中断状态)。

我认为,忽略 InterruptedException 是否安全取决于您在该线程中执行的操作。如果在任何情况下,您的线程的不需要的中断会使系统或任何资源处于不需要的状态(脏、锁定、未释放,这可能导致资源泄漏),那么您有责任处理 InterruptedException,您不应该忽略它。是否要使线程可中断取决于您。

换句话说,中断机制主要用于执行取消策略和任务清理。如果您在任务中没有任何需要清理的东西,或者您没有任何特定的任务取消政策,忽略 IntrruptedException 可能在政治上听起来不正确,但没关系。

if the thread is supposed to live as long as the JVM, meaning the thread is not interruptible, is it safe to assume that InterruptedException will never be thrown and therefore the exception can be swallowed?

我理解你的痛苦,没有商业价值的样板 try-catch 块的激增损害了代码。

如果您打算吞并异常的代码完全在您的控制之下,并且如果它只是 客户端代码(它永远不会在您自己以外的上下文中重用),那么吞下异常是安全的。请注意,那里有很多 if

如果您碰巧使用 Java 8,您还有另一种选择,如 here 所述:将您的代码包装在 uncheckCall(() -> { ... }) 块中。这让代码块抛出 InterruptedException 而无需声明它。正如链接答案中所解释的,这应该非常小心地处理,但它有可能使您的代码非常干净。

关于这些问题,我仍然认为圣经的书,Goetz 等人。 Java Concurrency in Practice 在第 7 章中说了以下重要内容(与 Goetz 的 developerWorks article 中的 material 几乎相同,它具有优势是免费的,但本书在某些方面更清楚一些。)

Interruption is a cooperative mechanism. One thread cannot force another to stop what it is doing and do something else; when thread A interrupts thread B, A is merely requesting that B stop what it is doing when it gets to a convenient stopping point—if it feels like it.

[...]

Only code that implements a thread’s interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests.

所以,是的,您可以 "swallow" InterruptedException 在您概述的情况下。

书中还有一个重点:

Because each thread has its own interruption policy, you should not interrupt a thread unless you know what interruption means to that thread.

因此您可以选择自己的策略,例如 "crashing" 和 RuntimeExceptionAssertionError,只需将其记录为警告,或者只要记录此问题就完全忽略中断对于其他可能需要知道的人。什么是最好的取决于你的线程真正在做什么,你没有提供任何细节。例如,如果它是 运行 在火箭上 [说做姿态控制],你不想让 JVM/rocket 崩溃只是因为一个程序员同事 [他甚至可能与你不在同一家公司工作,例如他写了一些你调用的库] 忘记了他代码中某处的某个合同,并在你的代码中转储了一个安全可忽略的异常:"The exception which occurred was not due to random failure but a design error."