处理线程中断时是否有'too much cleanup'之类的东西?



这篇关于处理中断的最佳实践的伟大文章提到了以下内容:

有时,在传播异常之前需要进行一些清理。在这种情况下,您可以捕获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;
         }
    }
}

例如,如果waitForPlayersstartNewGame可能引发其他异常(已检查或未检查),会发生什么情况?在这种情况下,你可能会失去球员。。。就像你有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

编号。如果你再次被打扰怎么办?如果你需要一个原子方法,那么你会遇到比传播异常更大的问题。

最新更新