【问题标题】:Proper way to handle Thread.interrupted() in a Callable?在 Callable 中处理 Thread.interrupted() 的正确方法?
【发布时间】:2012-09-30 02:25:02
【问题描述】:

在 Callable 中处理 Thread.interrupted() 的正确方法是什么?我猜这个callable应该抛出一个InterruptedException;例如:

public class MyCallable implements Callable<Object> {
    public Object call() {
        Object result = null;

        // Simulate long-running operation that calculates result
        while (true) {
            ...
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }

        result = ... // something produced by the long-running operation    

        return result;
    }
}

这是正确的,还是有更合适的方法来处理它?谢谢。

【问题讨论】:

  • 解释InterruptedException的一个真正来源是here
  • 我之前读过,但它没有提到 Callable。不过,非常好的文章。

标签: java multithreading future callable


【解决方案1】:

编辑

经过一些反复,您似乎希望能够中断您的 IO 例程。对于一些 NIO InterrutibleChannel 类来说,这似乎是一项不错的工作。例如,从下面的BufferedReader 读取是可中断的并且会抛出InterruptedIOException。在此处查看more examples of the NIO code

BufferedReader in = new BufferedReader(new InputStreamReader(
    Channels.newInputStream((new FileInputStream(
        new File(...))).getChannel())));

然后,您可以调用future.cancel(),这将中断您的线程并导致IO 抛出InterruptedIOException。如果发生这种情况,您可以捕获IOException 并让它从call() 方法中流出。


如果您想将call() 方法被中断的情况传回Future,那么我认为抛出InterruptedException 很好。另一种选择是仅使用 return null;call() 方法中的其他标记对象。如果线程被中断,我通常会这样做。

要记住的一点是,如果 call() 抛出 InterruptedException,当您执行 future.get() 时,它将抛出 ExecutionException,并且该异常的原因将是InterruptedException。如果get(long timeout, TimeUnit unit) 超时,请不要混淆future.get() 本身也可以抛出InterruptedException

 try {
     result = future.get();
 } catch (ExecutionException e) {
     if (e.getCause() instanceof InterruptedException) {
        // call() method was interrupted
     }
 } catch (InterruptedException e) {
     // get was interrupted
 }

但是,如果调用了 future.cancel(true),则 future.get() 将改为抛出 CancellationException

【讨论】:

  • 我将提供更多上下文。我的代码实际上并没有使用 while (true) 循环——它是从一个自定义 InputStream 中读取的,该 InputStream 会检查 read(byte) 中 Thread.interrupted() 的值。 read() 方法抛出 InterruptedException 以确保调用代码(从流中读取)正确关闭流。
  • 原来这样不行,因为read()只能抛出IOException。看起来我想要的可能是 InterruptedIOException。
  • 当然@Greg。另一个想法是使用蔚来支持的InterruptibleChannel
  • 我曾想过使用 InterruptibleChannel,但我不知道该怎么做(我的代码使用标准 java.io 输入/输出流)。
  • 只是为了关闭循环——我的最终解决方案是实现一个自定义 InputStream 类,该类包装另一个 InputStream 并在 read() 中调用 Thread.currentThread().isInterrupted()。如果 isInterrupted() 返回 true,则该方法将引发 InterruptedIOException。否则,它将 read() 委托给包装的流。它还将 close() 委托给包装的流。我对 OutputStream 做了同样的事情。
【解决方案2】:

这实际上取决于您希望线程如何等待get()。如果您希望等待线程抛出异常,那么您不想throw new InterruptedException

想象一下

try{
  future.get();
}catch(ExecutionException ex){

}catch(InterruptedException em){

}

如果发生任何异常,您希望它是什么?在您的情况下,它是ExecutionException。如果您不想要 ExecutionException,则不应重新抛出 InterruptedException。

【讨论】:

  • 在这种情况下,我的可调用对象是从 InputStream 中读取的。如果在 Future 实例上调用 cancel(),我想确保流已关闭(请参阅我对 Gray 回答的评论)。
  • 你有没有get对未来的看法?
  • 是的,最终我会在 Future 上调用 get()(如果 Future 没有被取消的话)。
  • 好的,如果 InterruptedException 在 call 方法之外被抛出,任何后续的 get 都会看到 InterruptedException,在我看来这似乎不适合等待线程。这也假设线程池线程将清理 Callable 的资源。如果等待线程会清理资源,那么获取 InterruptedException 可能是有意义的。
  • @Gray 如果取消导致 InterruptedException 在其中 Future 基本上被取消,似乎 CancellationExceptionExecutionException 之前被抛出
猜你喜欢
  • 1970-01-01
  • 2012-04-19
  • 1970-01-01
  • 2010-12-08
  • 2011-02-28
  • 2016-01-15
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
相关资源
最近更新 更多