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

Posted

技术标签:

【中文标题】如果没有人调用interrupt(),是不是可以忽略InterruptedException?【英文标题】:Is it OK to ignore InterruptedException if nobody calls interrupt()?如果没有人调用interrupt(),是否可以忽略InterruptedException? 【发布时间】:2015-04-17 04:00:30 【问题描述】:

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

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

【问题讨论】:

我认为实际的问题是:在没有代码生成的情况下,JVM 是否曾经抛出 InterruptedException(通过 Thread.interrupt() 等) 是的。这是另一种说法。但我也想探索“是否有一个不间断线程的用例”方面 反过来问:“如果我总是正确处理InterruptedExceptions,我的线程是否安全?” - 回答:不。有不可中断的 (API-) 例程,例如阻塞 IO,这将使在InterruptedExceptions 上的每一次善意尝试都是徒劳的。因此,忽略 InterruptedExceptions 通常不会破坏任何东西,即它是“安全的”。正如其他人所说,它很少是“可取的”。 一些不直接“处理”您的代码的容器/运行时包装器可能需要中断线程。例如,我正在使用 maven exec 插件,在我对吞下中断的异常进行更改后,当我尝试使用 sigterm 或 ^C 停止程序时,maven 会窒息。 【参考方案1】:

从不认为忽略已检查异常是安全的。 目前对您来说可能没问题,但如果其他程序员扩展您的代码,他会期望标准行为:线程对中断调用作出反应。 在这种情况下,空的 catch 块也是危险的,因为 JVM 删除了中断标志,并且肯定应该再次设置它

Thread.currentThread().interrupt();

在 catch 块中。在我看来,这是InterruptedExceptions 的最小捕获实现。在循环中检查 isInterrupted 标志也没有太大的伤害。 与您未来的程序员自己在一两天内寻找意外线程行为的麻烦相比,这几乎没有开销,因为您的项目可能已经增长了一点。

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

另一方面,InterruptedException 不会在硬件故障的情况下由 JVM 本身抛出,它只是用户指定的Exception因此,如果您不传播您的 Threads 引用,则不会有任何其他 Threads 能够在其上调用 Thread.interrupt() 就是这样技术上。 但您不应低估人为因素和程序的演变。编辑:正如 ruakh 指出的那样,实际上有一种方法获得Threads 参考,从而安排Thread.interrupt() 通话。这样一来,热火朝天的开发人员可能甚至不必查看实现了您的不间断Thread 的课程。在我看来,这甚至是实现正确异常处理的另一个原因。另一件事:如果您没有抛出 Exception,那么在超出 INFO 的级别上记录此类事件可能是个不错的选择。

【讨论】:

什么是“标准行为”?清理并关闭? @james large 事实上,可能没有标准的行为,而是预期的行为。即中止当前进程并优雅地完成。但一如既往:文档是这里的关键。 Re:“如果你不传播你的 Threads 引用,就不会有任何其他 Threads,可以在其上调用 Thread.interrupt()”:线程不是像随机对象一样,您可以决定是否传播。 JDK 提供了查找所有线程的方法。请参阅***.com/questions/1323408/… 了解一些方法。 @ruakh 感谢您的评论。我已经在我的回答中更正了这一点。 关于safeSleep方法:不用自己实现,也可以使用Guava的Uninterruptibles(如Uninterruptibles.sleepUninterruptibly【参考方案2】:

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

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

来自the Javadocs for Error:

错误是 Throwable 的子类,表示合理的应用程序不应尝试捕获的严重问题。大多数此类错误都是异常情况。

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

另一种看待这个问题的方式是,如果将来有人确实中断了您的方法,他们是否有可能希望它继续运行,就好像什么都没发生一样?我猜没有。

【讨论】:

我通常为此使用AssertionError,但这主要只是个人喜好。 然后,当它在六个月后崩溃时,因为你写了一些需要中断它的东西,你会记得为什么你决定不一开始就默默地接受错误。或者,您会遇到一个微妙的错误,例如数据未保存到数据库、内存泄漏或该线程可能持有的任何其他资源。 呃。这很难看,如果你要重新抛出InterruptedException,只需抛出InterruptedException 本身。对于库代码尤其如此。如果这是您的应用程序代码,那么如果您重新抛出或忽略异常,它真的不会有任何区别,因为您可以保证它不是首先引起的。另一种选择是优雅地退出您的线程。 @Dorus 问题是针对在线程内编写的代码。你不能从Runnable.run() 抛出InterruptedException,除非它被包裹在别的东西里面。如果您的意思是“将InterruptedException 放入您随后调用的某个方法中”,当然可以,但是您在调用它的线程中会做什么? 嗯是的,一旦你达到线程级别,崩溃或优雅地结束你的线程都是有效的选择。必须同意。选择最适合的。【参考方案3】:

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

你应该做的最低限度是:

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

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


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

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

当线程被中断时,此方法将简单地提前返回并保留Thread 的中断状态,因此它已经为您的代码被其他人使用的情况提供了正确的处理 em> 使用中断(假设你的代码调用者检查中断状态)。

【讨论】:

另外,如果您有理由相信您的代码可以安全地忽略某些异常,那么您至少应该调用 LOGGER.debug(ex),以便更容易弄清楚如果发生了什么你的信念应该以某种方式被证明是错误的。 @jameslarge 对我来说,只是打印一条日志消息仍然忽略异常 @Hilikus,我同意。 Holger 的回答解释了为什么忽略您认为永远不会发生的异常是一个坏主意。我在评论中添加了一些关于您知道异常发生,而您意味着忽略它的罕见情况。 +int(PI/3) for LockSupport - 你让我今天学到了 Java 的另一个不常见的特性......【参考方案4】:

Goetz 等人在这些问题上仍然认为圣经的书。 Java Concurrency in Practice 在第 7 章中提到了以下重要内容(在 Goetz 的 developerWorks article 中可以找到几乎相同的材料,它的优点是免费,但本书更清楚一点一些观点。)

中断是一种合作机制。一个线程不能强迫另一个线程 停止它正在做的事情并做其他事情;当线程 A 中断线程 B 时,A 只是请求 B 在它到达一个方便的停止点时停止它正在做的事情——如果它愿意的话。

[...]

只有实现线程中断策略的代码才能吞下一个线程 中断请求。通用任务和库代码不应该 吞下中断请求。

所以,是的,在您描述的情况下,您可以“吞下”InterruptedException

也是书中重要的一点:

因为每个线程都有自己的中断策略,所以不应该中断 一个线程,除非你知道中断对那个线程意味着什么。

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

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

它不应该被忽视。它应该被扔回给调用者,或者你应该重新断言线程的中断状态,当抛出异常时该状态被清除:

catch (InterruptedException e)  
     // Restore the interrupted status
     Thread.currentThread().interrupt();
 

除非你在catch之后及时退出线程。

Here的一篇关于与InterruptedExceptions打交道的文章。

【讨论】:

如果你只是重新抛出异常,那与使用已弃用的 Thread.stop() 方法杀死线程的效果不一样吗?谁来捕获异常?【参考方案7】:

如果线程应该与 JVM 一样长,这意味着线程不可中断,是否可以安全地假设 InterruptedException永远被抛出,因此可以吞下异常?

我理解你的痛苦,没有商业价值的样板 try-catch 块的扩散会伤害代码。

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

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

【讨论】:

【参考方案8】:

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

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

【讨论】:

以上是关于如果没有人调用interrupt(),是不是可以忽略InterruptedException?的主要内容,如果未能解决你的问题,请参考以下文章

奋斗吧,程序员——第三十八章 旧时茅店社林边,路转溪头忽见

如何确保没有人可以两次调用api

如果没有人调用该函数,为啥我会得到第一次机会异常?

yield函数 ,interrupt函数

在 boost 线程上调用抽象函数,不会在 interrupt_point 中断

调用Thread.interrupt()方法到底会发生什么?