在执行 `throw new InterruptedIOException()` 前,我应该执行 `Thread.currentThread().interrupt()` 吗?

17

我实现了MyInputStream.read()方法,并注意到该函数内可能会发生InterruptedException。经过一些搜索,我发现捕获InterruptedException并重新抛出InterruptedIOException是很常见的做法,大概像这样:

    try {
        ...
    } catch (InterruptedException e) {
        //Thread.currentThread().interrupt(); // <=== ???
        throw new InterruptedIOException();
    }

但只有约50%的代码示例使用Thread.currentThread().interrupt()

尽管吞下InterruptedException是最糟糕的事情,但在抛出其他异常之前,我应该重新中断当前线程吗?

正:单个中断请求可能有多个“接收者”。

反对:某些功能如日志记录可能在中断状态被清除之前不起作用,这可能会导致微妙的错误。

反对:我们收到关于中断请求的两个通知:线程的中断状态和异常。最初只有一个通知:要么线程的中断状态为true,要么抛出InterruptedException,但不会同时发生。

正:实际上没有人真正针对可能抛出的I/O异常测试代码,而InterruptedIOException与其他I/O异常不同;catch(IOException)子句可能会吞下中断状态。

PS 看起来无论我做什么,InterruptedIOException是一种非常特殊的要求特殊处理的IOException问题都不会解决。

PPS (编辑) 我不能让原始的InterruptedException传播,因为InputStream.read()不能抛出InterruptedException(它会throws IOException而不抛出其他任何内容)。 而我无法预测调用MyInputStream.read()的上下文:MyInputStream的一个实例可能被传递给任何接受InputStream参数的Java或第三方库函数。

至于旧错误,看起来它只是已关闭,而不是解决了;而中断状态的真正目的是使代码行为不同。


我不喜欢 InterruptedIOException 的原因是它不能方便地嵌入其他异常作为原因。根据上下文,我可能会只是 throw new IOException(e),这样当我在上层捕获异常时仍然可以获取到根本原因。 - Ibrahim Arief
@IbrahimArief 使用 initCause(...)。 三行代码就能完成任务:InterruptedIOException ioExc = new InterruptedIOException(); ioExc.initCause(e); throw ioExc; - Dan Armstrong
3个回答

2

我建议使用ClosedByInterruptException。虽然它属于NIO库,但它比旧的InterruptedIOException有更好定义的API,使得处理更加容易。例如:

try {
    ...
} catch (InterruptedException e) {
    close(); // obeying API of ClosedByInterruptException
    Thread.currentThread().interrupt(); // again, obeying API
    throw new ClosedByInterruptException();
}

参见 Thread#interrupt()。如果您想重新抛出原始异常,可以使用以下方法:

try {
    ...
} catch (InterruptedException e) {
    throw e;
}

所以您需要保留堆栈跟踪。在这种情况下,中断状态被清除,因为InterruptedException的API建议如此。


这个答案似乎是最好的。正如Stephen所说,关于中断状态和日志记录的发帖者的反对意见可以安全地忽略。而关于两个通知的反对意见则通过ClosedByInterruptException的明确而精确的含义得到了解决。 - Michael Allan

1
由于以下原因:
  1. 必须不吞咽InterruptedException

  2. 可以吞咽任何IOException(尤其是InterruptedIOException),即可被第三方代码消耗

  3. IOException旨在通知有关i/o发生的事件,而不是线程状态的更改

  4. 我们无法控制的某些代码可能会在抛出或重新抛出InterruptedIOException之前设置或清除线程中断状态

我决定:

  1. the current thread MUST be reinterrupted before throwing an InterruptedIOException.
  2. the code handling InterruptedIOException SHOULD NOT assume any particular state of the thread interrupted status
  3. this behavior MUST be documented:

    @throws InterruptedIOException if the current thread is interrupted (the thread
            interrupted status is true when InterruptedIOException is thrown)
    @throws IOException if this stream is closed or another IOException occurs.
    
    Note that due to possible interference from 3rd-party code, handlers 
    of InterruptedIOException should not assume any particular state
    of the thread interrupted status when they are invoked.
    

1
如果上层有代码需要检查中断标志,那么是的,在抛出不同的异常之前应该重新中断当前线程。否则,这并不重要。
然而,一些功能(如日志记录)在中断状态被清除之前可能无法正常工作,这可能会导致微妙的错误。
根据我对分析的理解,你所链接的错误只适用于Java 6及更早版本,并且仅适用于Solaris平台。现在你可以忽略它了。(除非你有一个拥有巨额资金的客户,否则你不应该为Java 6或更早版本编写重要的应用程序。)
我们会得到两个关于中断请求的通知:线程的中断状态和异常。它们是不同种类的通知...
实际上,一个更好的策略可能是在捕获InterruptedIOException时设置中断标志...或者简单地允许原始的Interrupted异常传播。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接