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

The*_*ode 54 java multithreading asynchronous executorservice executor

在将此标记为重复之前,请仔细阅读该问题.

下面是伪代码的片段.我的问题是 - 以下代码是否没有打败并行异步处理的概念?

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

我知道你可以等待有限的时间等,但如果我真的关心结果呢?如果要执行多个任务,问题会变得更糟.在我看来,我们只是同步地完成工作.我知道Guava库提供了非阻塞侦听器接口.但我很想知道我对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());
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ohn 45

Future为你提供isDone()没有阻塞的方法,如果计算完成则返回true,否则返回false.

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

你有几个选择:

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

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

使用CACHE示例进行更新

这是Java Concurrency In Practice中的一个缓存实现,是一个很好的用例Future.

  • 如果计算已经运行,对计算结果感兴趣的调用者将等待计算完成
  • 如果结果在缓存中就绪,则调用者将收集它
  • 如果结果尚未就绪且计算尚未开始,则调用者将开始计算并将结果换入Future其他调用者.

这很容易通过FutureAPI 实现.

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());
        }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 但是你还在等待循环检查isDone(),这不是阻塞吗? (4认同)
  • 你是对的.但是我仍然想知道这是不是很复杂.开箱即用的听众解决方案非常方便. (4认同)
  • 这个监听器很可能是一个简单的观察者模式中的观察者。您可能可以自己计算 20 分钟内的一个:) 这可能会有所帮助:http://www.tutorialspoint.com/design_pattern/observer_pattern.htm (3认同)
  • 这完全取决于您的设计。你不必。例如,您可以让一个线程在事件进入系统时分派事件,并定期检查结果。在这种情况下,如果结果未准备好,则检查结果不会阻塞,线程可以继续调度事件。这里可以使用许多模式来避免阻塞。检查可用于通知结果的观察者模式,还有活动对象模式和半同步 - 半异步,发布订阅。尝试阅读这些模式以了解如何使用异步编程。 (2认同)

Rav*_*abu 8

下面是伪代码的片段.我的问题是 - 以下代码是否没有打败并行异步处理的概念?

这一切都取决于您的用例:

  1. 如果你真的想阻止直到得到结果,请使用阻止 get()
  2. 如果您可以等待特定时间段来了解状态而不是无限阻塞持续时间,请使用get()超时
  3. 如果您可以继续而不立即分析结果并在将来检查结果,请使用CompletableFuture(java 8)

    可以显式完成的Future(设置其值和状态),并且可以用作CompletionStage,支持在完成时触发的依赖函数和操作.

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

    Java执行程序:如何在任务完成时通知而不阻塞?


NiV*_*VeR 5

我想在这个问题上分享更多的理论观点,因为已经有一些技术答案。我想根据评论来回答:

让我给你举个例子。我提交给服务的任务最终会引发 HTTP 请求,HTTP 请求的结果可能需要很多时间。但我确实需要每个 HTTP 请求的结果。任务在循环中提交。如果我等待每个任务返回(get),那么我在这里失去了并行性,不是吗?

这与问题中所说的一致。

假设你有三个孩子,你想为你的生日做一个蛋糕。由于您想制作最美味的蛋糕,因此您需要准备很多不同的东西。所以你要做的就是把配料分成三个不同的清单,因为你住的地方只有 3 家超市出售不同的产品,并为你的每个孩子分配一个任务,simultaneously

现在,在您开始准备蛋糕之前(让我们再次假设,您需要事先准备好所有食材),您将不得不等待走最长路线的孩子。现在,您需要在开始制作蛋糕之前等待所有成分,这一事实是您的必需品,而不是任务之间的依赖关系。您的孩子一直在尽可能长时间地同时完成任务(例如:直到第一个孩子完成任务)。所以,总而言之,这里有平行度。

当您有 1 个孩子并且您将所有三项任务分配给他/她时,将描述顺序示例。