是否有太多的清理'处理线程中断时?

时间:2014-10-11 00:39:49

标签: java multithreading interrupt-handling

This great article关于处理中断的最佳做法提到了以下内容:

  

有时需要在传播异常之前进行一些清理。在这种情况下,您可以捕获InterruptedException,执行清理,然后重新抛出异常。

然后,他继续举例说明捕获InterruptedException的方法,进行几行清理,然后向前传播异常。

他的小例子很有道理,但是假设我有一个更长的可中断方法,其任务不是那么简单,而且必须以原子方式执行。换句话说,中断时需要执行的“清理”量很大。这可以接受吗?如果是这样,我可以厚颜无耻地抓住中断,执行所有方法的正常工作流程(假装它是'清理'),然后在最后传播中断吗?

换句话说,我认为正确处理和传播中断很重要;我的问题是,以及时的方式处理中断有多重要,什么算是'及时'?

以下是我来自的示例(真实世界)场景:我有一个线程正在侦听消息队列;处理每个消息涉及多个HTTP调用和昂贵的数据库操作,并且,正如目前(不幸的)设计的那样,这些操作必须全部以原子方式执行。我可以将我的线程的中断处理行为定义为:'在被中断时,在传播中断之前完成你正常做的所有事情',或者这会使'cleanup'的定义有点过分?

4 个答案:

答案 0 :(得分:2)

您描述的方法有时被称为"进入“跛脚鸭”'模式",其中您将完成您已经开始但尚未接受或启动任何新工作的内容。

只要您记录它就可以了,这样来电者就知道会发生什么。遇到InterruptedException意味着某些上游调用者想要终止线程的活动,但安全性胜过响应。如果您认为这些操作必须一起完成(尽可能完全),并且只完成部分工作而停止工作单元会违反某些要求,那么您有权遵守这些要求并将它们放入高于暗示要求及时与中断请求合作。

理想情况下,您可以停止该交易的任何进一步进展,并尝试回滚您已完成的内容。然而,这种设计有其微妙之处;你可以做得足够远,只要完成交易就会比回滚几乎完成的成就更快。

同样,这里的关键是文档。如果您记录行为并发现您的呼叫者抱怨,那么您必须推迟竞争原子性的竞争要求。

答案 1 :(得分:2)

我认为没有任何有用的概念"太少"或者"太多"清理。当然,没有一般的方法来决定你做得太少或太多。

具体......

  

我可以将我的线程的中断处理行为定义为:'当被中断时,在传播中断之前完成你正常做的所有事情,或者这样做会拉伸定义'清理'有点太多了?

对此没有明确的答案。如果它有意义(例如需要这种行为),那么这样做是正确的。是否致电它"清理"与否是无关紧要的。

另一方面,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

我的观点......如果你担心一般(或" atomic")使代码具有弹性那么最好使用{{1阻止进行恢复; e.g。

finally

如果你需要进行原子操作,这也需要改变JVM之外的状态和/或"应用程序" ......那么即使 finally { // Make sure that we *always* put the players back if (playerOne != null) players.addFirst(playerOne); if (playerTwo != null) players.addFirst(playerTwo); } 也不够。在某些情况下,finally块中的代码不会被执行;例如如果JVM崩溃或被finally终止。 (这是@EJP的观点......)

答案 2 :(得分:1)

同一篇文章的结尾讨论了"不可解决的任务",它们在响应中断之前完成了他们正在做的事情(即使可能需要很长时间)。这听起来像是你拥有的。

你不必立即中止你正在做的事情,但是你应该设置一个标志来记住请求了一个中断,然后在原子时重新抛出InterruptedException工作已经完成。

答案 3 :(得分:0)

没有。如果再次被打断怎么办?如果你需要一个原子方法,那么你比传播异常要大得多。