如何正确处理可能未传递给客户端代码的InterruptedException?

时间:2016-04-23 08:19:08

标签: java interrupted-exception

我偶然发现了我必须处理InterruptedException的情况,但无法向上传递,并且不认为我的代码应该被允许吞下它。确切地说,我正在开发分布式锁实现,并且支持服务的请求可能会中断甚至超时 - 当然,java.util.concurrent.Lock不会解释这种情况并且不允许我吐出InterruptedException。我正在努力为非投掷lock()tryLock()unlock()方法编写正确的实现。

所以,问题是 - 处理这种情况的正确策略是什么?从目前的角度来看,我只看到三种选择(我觉得每种选择都有气味):

  1. 忽略lock / tryLock / unlock方法中的中断异常,重试/返回false /假设即使请求未到达目的地,TTL最终也会解锁记录。这显然不是最好的解决方案,因为它希望一切都好,而不是处理问题。
  2. 包裹RuntimeException继承人。这似乎也是一个非常糟糕的解决方案,因为客户端代码必须使用具体的实现而不是原始接口,并且未经检查的异常当然不是出于这样的目的。
  3. 使用 force Thread.currentThread().interrupt()调用。我不喜欢这种方式,因为它基本上告诉线程处理它自己的中断,而不是传递关于呼叫被中断的通知;另外,据我所知,如果没有外部轮询,它最终会生成线程,但不会立即处理中断,可能是在另一个地方。
  4. (当然,有一个选项允许客户端代码配置所需的行为,但这仍然没有为我提供一个非常好的解决方案)

    有没有比我所描述的更好的方式?如果不是,哪一个应该优先于其他人?

3 个答案:

答案 0 :(得分:4)

让我讨论您的每个可用选项。

  
      
  1. 忽略中断的异常
  2.   

这是错误的。当你实现像其他用户将依赖的库这样的东西时,永远不会吞下异常。在这些情况下,除非将其作为向客户端提供更有意义信息的其他异常传播,否则永远不会谨慎地吞下异常。 InterruptedException基本上是取消您的线程的请求,并且永远不应该从客户端抑制此信息,而不管锁定是否稍后将被解锁。客户需要知道有人希望此线程执行的工作单元被停止。

  
      
  1. 换入RuntimeException
  2.   

没有。出于与上述完全相同的原因,这也是错误的。传播InterruptedException的原因是让客户端知道已经请求取消正在执行的线程,因此将其包装在RuntimeException中是错误的,因为此信息丢失了。

  
      
  1. 使用/强制Thread.currentThread().interrupt()致电
  2.   

根据使用情况,这可能是对或错。问问自己是否可以传播InterruptedException。

  • 如果可以这样做(在你的情况下不是这样),那么你可以声明你的方法抛出InterruptedException并让上面的调用者担心需要做什么。通常情况下,当您调用抛出operation()的方法(比如InterruptedException)时,除非此调用完成,否则您将无法继续进行操作。假设operation()抛出InterruptedException,那么除了传播此异常之外,您无能为力。所以你不应该抓住异常。在这种情况下,只需声明您的方法抛出InterruptedException即可完成

  • 如果不能这样做,那么处理它的正确方法是强制进行interrupt()调用。使用此方法可以抑制异常,但仍然可以让客户端选择检查标志以查看是否发出了中断请求。而你是对的。这要求客户端轮询而不是处理中断。但这没有错。如果您不希望客户端轮询,那么传播异常将是更好的选择。但这并不总是可行的,你的例子就是这样一个用例。在许多情况下,执行线程即使在中断时也可以返回一些有意义的信息。因此,在这种情况下,异常被抑制,但是仍然可以通过调用interrupt()方法传递有关终止请求的信息。因此,客户端可以只使用从部分计算或轮询返回的结果来检查是否根据用例设置了中断标志。因此,通过这样做,您可以为客户提供更大的灵活性。

答案 1 :(得分:2)

对我来说,答案几乎总是3。

Java使用合作中断模型:对我来说,这感觉就像一种非常英国化的方法:

  

我说,老兄,你介意停止你正在做的事,如果不是太麻烦吗?

但是没有必要及时(或者实际上)根据中断采取行动。要使用Robin Williams quote

  

停止! ......或者......我会说再次停止!

你可以编写你的代码来定期检查是否有中断 - 如果你到处都这样做,它会变得非常混乱和重复。但是,如果您在中断时不想做任何事情,至少应该保留发生的中断这一事实,以便调用所做的代码我想做点相应的事情。

InterruptedException没有什么特别之处 - 它实际上是Exception的空子类。如果特定方法检查Thread.interrupted().isInterrupted(),通常只会抛出它。所以,我不担心调用interrupt()不会立即导致线程停止它正在做的事情 - 这就是合作中断的本质。​​

要说明为什么我在上面说“几乎总是”:Java tutorial因此描述了中断:

  

中断是一个线程的指示,它应该停止它正在做的事情并做其他事情。由程序员决定线程如何响应中断,但线程终止是很常见的。

除了想要终止一个被打断的线程之外,我只做了非常非常少的事情。

如果我想要一个线程“做其他事情”,我可能会使用执行器服务:每个要做的“事情”由一个单独的Runnable表示,所以我不这样做甚至知道它们是否在同一个线程中完成。

所以我通常会中断线程,然后抛出RuntimeException

catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  throw new RuntimeException(e);
}

每个Runnable只会在中断时完成它正在做的事情;遗嘱执行人服务决定是否再做其他任务。

基本上只有在编写框架级代码(如ExecutorService)时才会选择在中断后继续。

答案 2 :(得分:1)

这完全取决于您的应用程序,尤其是您的线程中InterruptedException的含义。

对我来说,选项1是一种不好的做法:依赖其他机制会使您的代码不清晰和可怕。您始终可以捕获异常并仅使用finally方法释放所有锁定的资源。

然后重新启动异常或隐藏异常(可能返回特定结果)取决于您以及该异常在您的应用程序中具有的特定含义。