Future.cancel(true)不能可靠地取消/中断线程

时间:2013-12-11 21:04:39

标签: java multithreading

我尝试与CompletionService并行执行多个任务。当我尝试实施取消时,问题就出现了。

以下是我使用的代码草图:

void startTasks(int numberOfTasks) throws Exception {

    ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
    CompletionService<TaskResultType> completionService = new ExecutorCompletionService<TaskResultType>(executor);
    ConcurrentLinkedQueue<TaskResultType> results = new ConcurrentLinkedQueue<BenchmarkResult>();
    ArrayList<Future> futures = new ArrayList<Future>();

    for (int i = 0; i < numberOfTasks ; i++) {
        TypeOfTask task = ... ; 
        Future future = completionService.submit(task);
        futures.add(future);
    }

    boolean failed = false;
    Throwable cause = null;
    for (int i = 0; i < numberOfThreads; i++) {
        try {
            Future<TaskResultType> resultFuture = completionService.take();
            TaskResultType result = resultFuture.get();
            results.add(result);
        } catch (ExecutionException e) {
            failed = true;
            cause = e.getCause();
            /* cancel all other running tasks in case of failure in one task */
            for (Future future : futures) {
                future.cancel(true);
            }
        } catch (CancellationException e) {
            // consume (planned cancellation from calling future.cancel())
        }

    }
    executor.shutdown();
    // code to throw an exception using cause
}

任务实施Callable

当我现在在大多数情况下在其中一个任务中抛出一个异常时,它运行正常,即我立即从其他任务中获取CancellationExceptions并立即完成任务(让我们调用这个案例A)。但有时(让我们称之为B),一些任务先完成,然后抛出CancellationException。 Future.cancel(true)在所有任务的两种情况下都返回true(除了具有初始ExecutionException的那个,因为这个已被取消)。

我使用Thread.currentThread.isInterrupted()检查中断标志,在完成的任务中(即取消不成功的任务),中断标志设置为false。

在我看来,这一切似乎都是非常奇怪的行为。任何人都知道问题可能是什么?

更新

到目前为止,我所知道的最好的想法是,在包含任务的代码深处某处(只有一些高级代码来自我自己),中断状态被消耗,例如,通过捕获的InterruptedException,它不会调用Thread.interrupt()来重新建立状态。 Future.cancel()设置中断标志的确切时间可能会因线程的调度而略有不同,这可以解释不一致的行为。消耗的中断状态会解释案例B的行为吗?

2 个答案:

答案 0 :(得分:0)

  

但有时,某些任务先完成,然后抛出CancellationException。 Future.cancel(true)在所有任务的两种情况下都返回true(除了具有初始ExecutionException的那个,因为这个已被取消)。

如果您的程序没有完成(或者在抛出异常后需要很长时间才能完成),那么我怀疑您的问题是其中一个任务正在执行IO或以其他方式阻止,并且没有检查Thread.currentThread().isInterrupted() 。因此,即使您已取消Future并且线程被中断,也不会被检测到。

然而,似乎该计划正在完成。所以我不确定这里的错误案例是什么。如果您发现异常,请在列表中的所有期货上调用future.cancel(true)。扔掉的那个和已经完成的那个都应该返回false因为它们不能被取消。从取消返回true的那些应该被中断。

例如。如果第二个到最后一个线程抛出一个异常,那么future.cancel(true)应该仅为最后一个运行的线程返回true

要做的一件事是在完成时删除期货,这样您就不会重新取消已经完成的工作。但所有可能做的就是掩盖你现在看到的问题:

Future<TaskResultType> resultFuture = completionService.take();
futures.remove(resultFuture);

<强>更新

有些代码很可能吞下中断。不幸的是,它一直都在发生。如果某些线程在取消并且正在运行完成时没有立即完成,那么这可能就是正在发生的事情。

答案 1 :(得分:0)

  

但有时候,有些任务会先完成然后抛出   CancellationException。

可能是你取消任务的情况,它通常是中断的(在这种情况下你可能认为它返回了一个结果,但是对于CompletionService它被取消了),take()正在返回未来,您拨打future.get()并且有CancellationException

您还可以查看Guava的Futures.allAsList,它似乎做了类似的事情:

  

创建一个新的ListenableFuture,其值为包含所有输入期货值的列表(如果全部成功)。如果任何输入失败,则返回的未来将失败。