Java长运行任务线程中断与取消标志的比较

问题描述 投票:15回答:5

我有一个长期运行的任务,类似于。

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

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

谢谢。

杰夫

java concurrency multithreading interrupt
5个回答
7
投票

中断将把线程从指定的等待条件列表中轰出来。自己的取消标志不会。如果你想中断对IO和事件的等待,使用interrupt。否则就使用你自己的。


21
投票

使用中断的一个问题是 如果你不能控制所有的代码被执行你有可能会因为以下原因而导致中断不能 "正常 "工作。别人对处理中断的错误理解。 的库中。这就是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! */ }
    }
}

这个对中断的 "不恰当处理 "导致你的程序无限期循环 再次强调,不要把这当做可笑的事情,有很多破损的中断处理机制。

至少使用你自己的标志会让任何第三方库完全看不到。


1
投票

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

所以,如果工作中涉及到潜在的阻塞活动,你需要考虑到中断的问题。纵然 你决定使用一个标志来控制进程,并且应该适当地捕获和处理propagate中断。

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


0
投票

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


0
投票

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

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

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

其次,我们来研究一下一般的惯例。

Java类库一般将线程中断解释为取消请求。比如说 LinkedBlockingQueue.take 可以使我们的程序阻塞。但是当当前线程被打断时,它会抛出一个 InterruptedException 并返回。所以我们的应用程序通过使用响应式方法成为响应式。所以我们可以在已有的支持基础上,再编写额外的 Thread.interrupted/Thread.currentThread().isInterrupted 我们的代码中的检查。

此外,我们代码中的取消方法 ExecutorService 使用线程中断。正如我们提到的。Future.cancelExecutorService.shutdownNow 依靠中断。

© www.soinside.com 2019 - 2024. All rights reserved.