16

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

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

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

谢谢,

杰夫

4

5 回答 5

22

使用中断的一个问题是,如果您不控制所有正在执行的代码,您将面临中断无法“正常”工作的风险,因为其他人对如何处理他们的库中的中断有错误的理解。那就是 API无形地导出一个 API,围绕它对interrupt你变得依赖的 s 的处理。

在您的示例中,假设doSomeWork位于第 3 方 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! */ }
    }
}

这种对中断的“不当处理”会导致您的程序无限循环。同样,不要认为这是荒谬的;那里有很多损坏的中断处理机制。

至少使用您自己的标志对任何第 3 方库来说是完全不可见的。

于 2009-12-16T14:35:39.300 回答
8

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

于 2009-12-16T14:17:28.057 回答
1

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

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

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

于 2009-12-16T14:34:57.987 回答
0

我认为在大多数情况下这是一个偏好问题。我个人会选择手工制作的旗帜。它给了你更多的控制权——例如,这样你可以确保你的线程不会让其他对象处于不一致的状态。此外,如果性能真的很关键,请记住使用异常会产生开销(即使在 99% 的情况下可以忽略不计)。

于 2009-12-16T14:29:42.790 回答
0

首先,我们来看看使用条件。

如果我们有一个线程池,并且使用中断作为取消机制,我们只能通过池中断工作线程。换句话说,我们不能直接调用Thread.interrupt,因为我们不拥有线程。因此,我们必须获取 aFuture并调用Future.cancel。或者我们必须调用ExecutorService.shutdownNow取消所有中断繁忙线程的任务。在第一种情况下,我们需要一些簿记来握住Future把手。因此应用程序必须保留新任务并删除旧任务。

另一方面,如果您使用全局取消标志,我们可以从一个中心位置取消/停止多个任务,而无需额外记账。但是如果我们想取消单个任务——类似于调用Future.cancel——我们必须为每个任务存储一个取消标志。

其次,让我们检查一下一般约定。

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

而且,取消方法在ExecutorService使用线程中断。正如我们所提到的,Future.cancel依赖ExecutorService.shutdownNow于中断。

于 2020-06-12T08:20:58.700 回答