这篇关于处理中断的最佳实践的伟大文章提到了以下内容:
有时,在传播异常之前需要进行一些清理。在这种情况下,您可以捕获InterruptedException,执行清理,然后重新引发异常。
然后,他给出了一个方法的例子,该方法捕获InterruptedException,执行几行清理,然后继续传播异常。
他的小例子很有道理,但假设我有一个更长的可中断方法,它的任务并不那么简单,必须以原子的方式执行。换句话说,当中断时,它需要执行的"清理"量是巨大的。这可以接受吗?如果是这样的话,我能厚着脸皮抓住中断,执行该方法的所有正常工作流程(假装它是"清理"),然后在最后传播中断吗?
换句话说,我明白正确地处理和传播中断是很重要的;我的问题是,以及时的方式处理中断有多重要,什么才算"及时"
以下是我来自的示例(现实世界)场景:我有一个线程在侦听消息队列;处理每条消息涉及多个HTTP调用和昂贵的DB操作,而且,正如目前(不幸的是)设计的那样,这些操作都必须以原子方式执行。我可以将线程的中断处理行为定义为:"当被中断时,在传播中断之前,像往常一样完成你正在做的一切"吗?还是这对"清理"的定义有点过分了?
我认为没有任何有用的"太少"或"太多"清理概念。当然,没有一般的方法可以决定你做得太少或太多。
具体来说。。。
我可以将线程的中断处理行为定义为:"当被中断时,在传播中断之前,像往常一样完成你正在做的一切"吗?还是这对"清理"的定义有点过分了?
对此没有确切的答案。如果这是有意义的(例如,这种行为是必需的),那么这样做是正确的。无论您是否调用它"cleanup"都无关紧要。
另一方面,Java中断的一个常见用例是向应用程序的某个部分发出信号,停止它正在做的事情,例如:
- 服务器正在关闭,或者
- 请求的操作花费的时间太长,或者
- 提出请求的客户端已经"离开",没有其他原因可以完成请求
在这种情况下,"一切正常"可能是错误的策略,尤其是在成本高昂的情况下。(或者这可能是正确的策略;例如,如果没有可靠的方法退出需要原子化完成的行动序列。)
简而言之。。。我们不能告诉你这样做是否正确。
换句话说,我明白正确处理和传播中断是很重要的;我的问题是,及时处理中断有多重要,什么才算"及时"?
再说一遍。这些问题只有在您的应用程序的上下文中才有意义(并且只能回答)。这取决于。。。
但我不认为这个(清理)仅限于中断。考虑一下文章中的例子:
public class PlayerMatcher {
private PlayerSource players;
public PlayerMatcher(PlayerSource players) {
this.players = players;
}
public void matchPlayers() throws InterruptedException {
Player playerOne, playerTwo;
try {
while (true) {
playerOne = playerTwo = null;
// Wait for two players to arrive and start a new game
playerOne = players.waitForPlayer(); // could throw IE
playerTwo = players.waitForPlayer(); // could throw IE
startNewGame(playerOne, playerTwo);
}
}
catch (InterruptedException e) {
// If we got one player and were interrupted, put that player back
if (playerOne != null)
players.addFirst(playerOne);
// Then propagate the exception
throw e;
}
}
}
例如,如果waitForPlayers
或startNewGame
可能引发其他异常(已检查或未检查),会发生什么情况?在这种情况下,你可能会失去球员。。。就像你有InterruptedException
一样。
我的观点。。。如果您关心的是使代码在一般情况下具有弹性(或"原子"),那么最好使用finally
块来进行恢复;例如
finally {
// Make sure that we *always* put the players back
if (playerOne != null)
players.addFirst(playerOne);
if (playerTwo != null)
players.addFirst(playerTwo);
}
如果您需要执行原子操作,也需要更改JVM和/或"应用程序"之外的状态。。。则即使是CCD_ 5也是不够的。在某些情况下,finally
块中的代码将不会被执行;例如如果JVM崩溃或被CCD_ 7终止。(这是@EJP的点…)
您描述的方法有时被称为"进入‘跛脚鸭’模式",在这种模式下,您将完成已经开始的工作,但不会接受或启动任何新工作。
只要你把它记录下来就可以了,这样打电话的人就知道会发生什么。遇到InterruptedException
意味着一些上游调用方想要终止线程的活动,但安全性胜过响应性。如果您认为这些操作必须一起完成(尽您所能),并且仅完成部分工作就停止工作单元将违反某些要求,那么您有权遵守这些要求,并将其置于隐含的要求之上,以便及时配合中断请求。
理想情况下,您应该停止事务的任何进一步进展,并尝试回滚您已经完成的内容。然而,这种设计也有微妙之处;你可能已经走得够远了,仅仅完成交易比收回你几乎完成的成就要快。
同样,这里的关键是文档。如果您记录了行为并发现调用方抱怨,那么您必须重新考虑事务原子性的竞争要求。
同一篇文章的结尾讨论了"不可取消任务",即在响应中断之前完成他们正在做的事情(即使可能需要很长时间)。听起来你就是这样。
你不一定要立即中止你正在做的事情,但你应该设置一个标志来记住请求了中断,然后在原子工作完成后重新抛出InterruptedException
。
编号。如果你再次被打扰怎么办?如果你需要一个原子方法,那么你会遇到比传播异常更大的问题。