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

时间:2009-12-16 14:14:36

标签: java concurrency multithreading interrupt

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

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

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

感谢,

杰夫

5 个答案:

答案 0 :(得分:20)

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

在您的示例中,假设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! */ }
    }
}

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

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

答案 1 :(得分:6)

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

首先,让我们看一下使用条件。

如果我们有一个线程池并使用interruption as the cancellation mechanism,则只能通过该线程中断工作线程。换句话说,由于我们不拥有线程,因此无法直接调用Thread.interrupt。因此,我们必须获取一个Future并调用Future.cancel。或者,我们必须调用ExecutorService.shutdownNow来取消所有中断繁忙线程的任务。在第一种情况下,需要握有Future手柄的一边进行簿记。因此,应用程序必须保留新任务并删除旧任务。

另一方面,如果您使用global cancellation flag,我们可以从中心位置取消/停止多个任务,而无需进行额外的簿记。但是,如果要取消单个任务(类似于调用Future.cancel),则必须为每个任务存储一个取消标志。

其次,让我们研究一下通用约定。

Java类库通常将线程中断解释为取消请求。例如,LinkedBlockingQueue.take可以使我们的程序块。但是,当当前线程中断时,它将抛出InterruptedException并返回。因此,我们的应用程序通过使用响应方法变得响应。因此,我们可以在现有支持的基础上,在我们的代码中编写其他Thread.interrupted/Thread.currentThread().isInterrupted检查。

此外,ExecutorService中的取消方法使用线程中断。如前所述,Future.cancelExecutorService.shutdownNow依赖于中断。