ExecutorService.invokeAll(timeout,TimeUnit)vs Future.get(timeout,TimeUnit)

时间:2017-12-20 13:27:32

标签: java concurrency java.util.concurrent

我正在使用 ExecutorService.invokeAll(Callable,timeout,TimeUnit)方法

在提交给ExecutorService的每个Callable中,我有一个 future.get()

即使executorService超时, future.get()仍会在后台运行吗?

我是否必须在 future.get(timeout,TimeUnit)上指定超时并抛出 TimeoutException 以确保将来终止?

2 个答案:

答案 0 :(得分:1)

正如the documentation所述:

  

返回后,未完成的任务将被取消。

没有明确说明,但它使用Future.cancel(true),即中断正在运行的任务。由于Future.get()支持中断,因此可以通过抛出InterruptedException来完成此操作。如果您的Callable没有捕获它,也没有做任何可能重置中断状态的事情,这意味着如果指定给Future.get()的超时已经过去,则invokeAll中的callables将停止等待。

然而,invokeAll仅取消期货,因此,发送中断信号但不等待线程对其做出反应并完成Callable代码的执行。因此,当invokeAll由于超时而完成时,某些线程可能仍然在已经取消的任务上运行。但如果这些任务仅由future::get组成,那么这应该不是问题。

但是如果你想要做的就是等待现有Future列表的完成,你可以更有效率地做到这一点。毕竟,您将每个Future包装到Callable调用Future.get中,invokeAll将包装到另一个Future中,可能会阻止每个未来的一个工作线程,然后等待所有这些未来的完成。最后一步正是这项任务的内容,所以你可以在没有前面步骤的情况下完成,例如

public static void waitForAll(Collection<? extends Future<?>> futures,
                              long timeout, TimeUnit unit)
    throws InterruptedException {

    long nanos = unit.toNanos(timeout);
    boolean done = false;
    try {
        final long deadline = System.nanoTime() + nanos;
        final int size = futures.size();
        for(Future<?> f: futures) {
            if(!f.isDone()) {
                if (nanos <= 0L) return;
                try { f.get(nanos, TimeUnit.NANOSECONDS); }
                catch(CancellationException | ExecutionException ignore) {}
                catch(TimeoutException toe) { return; }
                nanos = deadline - System.nanoTime();
            }
        }
        done = true;
    }
    finally { if (!done) for(Future<?> f: futures) f.cancel(true); }
}

这基本上是AbstractExecutorService在提交所有Callable之后等待所有期货完成的方式,为每个期货创建Future。如上所述,如果您想要做的就是等待现有的期货清单,您可以直接执行此操作而不会通过向执行人员提交工作来浪费资源。使用这种方法的另一个好处是,这将取消原始期货,而不仅仅是取消等待期货的工作。您也可以依赖此方法中断取消,而invokeAll未明确指定此属性,

答案 1 :(得分:0)

来自Javadoc

  

执行给定的任务,在完成或超时到期时返回持有其状态和结果的Futures列表

所以1.所有结果都将由所有Callable提供,包括内部future.get()调用的结果,以及2.一旦invokeAll返回(在超时之前),所有结果都是期货将会终止。