对 Future.get() 块的方法调用。这真的可取吗?

在将其标记为重复之前,请仔细阅读问题。

下面是伪代码的片段。我的问题是 - 下面的代码是否不能破坏并行异步处理的概念?

我问这个问题的原因是,在下面的代码中,主线程将提交一个要在不同线程中执行的任务。在队列中提交任务后,它会在 Future.get() 方法上阻止任务返回值。我宁愿在主线程中执行任务,而不是提交到其他线程并等待结果。通过在新线程中执行任务,我获得了什么?

我知道你可以等待有限的时间等,但是如果我真的关心结果呢?如果要执行多个任务,则问题会变得更糟。在我看来,我们只是在同步地做这项工作。我知道番石榴库提供了一个非阻塞的监听器接口。但我很想知道我的理解对于Future.get()API是否正确。如果它是正确的,为什么Future.get()被设计成阻止从而击败并行处理的整个过程?

注意 - 为了记录,我使用JAVA 6

public static void main(String[] args){

private ExectorService executorService = ...

Future future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});

System.out.println("future.get() = " + future.get());
}

答案 1

Future为您提供不阻塞的方法,如果计算已完成,则返回 true,否则返回 false。isDone()

Future.get()用于检索计算结果。

您有几种选择:

  • 调用,如果结果准备就绪,请通过调用来请求它,注意如何没有阻塞isDone()get()
  • 无限期地阻止get()
  • 用于指定超时的块get(long timeout, TimeUnit unit)

整个事情就是要有简单的方法从执行并行任务的线程中获取值。如果您愿意,可以同步或异步完成此操作,如上面的项目符号中所述。Future API

使用缓存示例进行更新

下面是 Java 并发实践中的缓存实现,这是 一个很好的用例。Future

  • 如果计算已经在运行,则对计算结果感兴趣的调用方将等待计算完成
  • 如果结果在缓存中准备就绪,则调用方将收集它
  • 如果结果尚未准备就绪且计算尚未开始,则调用方将开始计算并为其他调用方包装结果。Future

这一切都可以通过API轻松实现。Future

package net.jcip.examples;

import java.util.concurrent.*;
/**
 * Memoizer
 * <p/>
 * Final implementation of Memoizer
 *
 * @author Brian Goetz and Tim Peierls
 */
public class Memoizer <A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
            = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

public Memoizer(Computable<A, V> c) {
    this.c = c;
}

public V compute(final A arg) throws InterruptedException {
    while (true) {

        Future<V> f = cache.get(arg);
        // computation not started
        if (f == null) {
            Callable<V> eval = new Callable<V>() {
                public V call() throws InterruptedException {
                    return c.compute(arg);
                }
            };

            FutureTask<V> ft = new FutureTask<V>(eval);
            f = cache.putIfAbsent(arg, ft);
            // start computation if it's not started in the meantime
            if (f == null) {
                f = ft;
                ft.run();
            }
        }

        // get result if ready, otherwise block and wait
        try {
            return f.get();
        } catch (CancellationException e) {
            cache.remove(arg, f);
        } catch (ExecutionException e) {
            throw LaunderThrowable.launderThrowable(e.getCause());
        }
    }
  }
}

答案 2

下面是伪代码的片段。我的问题是 - 下面的代码是否不能破坏并行异步处理的概念?

这完全取决于您的使用案例:

  1. 如果您真的想阻止直到获得结果,请使用阻止get()

  2. 如果您可以等待特定时间段而不是无限的阻塞持续时间,请使用超时get()

  3. 如果您可以继续而不立即分析结果并在将来检查结果,请使用ComppletableFuture(java 8)

    可以显式完成(设置其值和状态)的未来,并且可以用作完成阶段,支持在完成时触发的依赖功能和操作。

  4. 您可以从 Runnable/Callable 实现回调机制。看看下面的SE问题:

    Java执行器:当任务完成时,如何在不阻塞的情况下收到通知?


推荐