Java长时间运行的任务线程中断vs取消标志

我有一个长期运行的任务,如:

public void myCancellableTask() { while ( someCondition ) { checkIfCancelRequested(); doSomeWork(); } } 

任务可以取消(请求取消,checkIfCancelRequested()检查取消标志)。 通常当我写这样的可取消循环时,我使用一个标志来表示已经请求取消。 但是,我知道我也可以使用Thread.interrupt并检查线程是否被中断。 我不确定哪种方法是首选,为什么,想法?

谢谢,

杰夫

中断会将线程从指定的等待条件列表中删除。 你自己的取消标志不会。 如果要中断IO和事件的等待,请使用中断。 否则使用自己的。

使用中断的一个问题是, 如果你不控制正在执行的所有代码 ,你就会冒中断不能正常工作的风险,因为别人对如何处理库中的中断有了深刻的理解 。 这就是API 无形地导出一个API来处理你所依赖的interrupt

在您的示例中,假设doSomeWork位于第三方JAR中,并且看起来像:

 public void doSomeWork() { try { api.callAndWaitAnswer() ; } catch (InterruptedException e) { throw new AssertionError(); } } 

现在你必须处理一个AssertionError (或者你正在使用的库可能抛出的任何其他东西)。 我见过有经验的开发人员在收到中断时会抛出各种废话! 另一方面,也许方法看起来像这样:

 public void doSomeWork() { while (true) { try { return api.callAndWaitAnswer() ; } catch (InterruptedException e) { /* retry! */ } } } 

这种“不正确的中断处理”会导致程序无限循环。 再次,不要认为这是荒谬的; 那里有很多破坏的中断处理机制。

至少使用自己的标志对任何第三方库都是完全不可见的。

这取决于doSomeWork()实现。 这是纯计算还是(在任何时候)涉及阻止API(例如IO)调用? 根据bmargulies的回答,JDK中的许多阻塞API都是可中断的,并且会将中断的exception传播到堆栈中。

因此,如果工作需要潜在的阻塞活动, 即使您决定使用标志来控制进程,也需要考虑中断,并且应该适当地捕获和处理/传播中断。

除此之外,如果依赖于标志,请确保使用volatile语义声明您的标志。

在大多数情况下,我认为这是一个偏好问题。 我个人会去手工制作的旗帜。 它为您提供了更多控制 – 例如,这样您就可以确保您的线程不会将其他对象置于不一致状态。 此外,如果性能非常重要,请记住使用exception会产生开销(即使在99%的情况下可以忽略不计)。