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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31092067/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 10:37:32  来源:igfitidea点击:

Method call to Future.get() blocks. Is that really desirable?

javamultithreadingasynchronousexecutorserviceexecutor

提问by TheMonkWhoSoldHisCode

Please read the question carefully before marking this as duplicate.

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

Below is the snippet of the pseudo code. My question is- Does the below code not defeat the very notion of parallel asynchronous processing?

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

The reason I ask this is because in the below code the main thread would submit a task to be executed in a different thread. After submitting the task in the queue, it blocks on Future.get() method for the task to return the value. I would rather have the task executed in the main thread rather than submitting to a different thread and waiting for the results. What is that I gained by executing the task in a new thread?

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

I am aware that you could wait for a limited time etc, but then what if I really care about the result? The problem gets worse if there are multiple tasks to be executed. It seems to me that we are just doing the work synchronously. I am aware of the Guava library which provides a non blocking listener interface. But I am interested to know if my understanding is correct for the Future.get() API. If it is correct, why is the Future.get() designed to block thereby defeating the whole process of parallel processing?

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

Note - For the record, I use JAVA 6

注 - 作为记录,我使用 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());
}

采纳答案by John

Futureoffers you method isDone()which is not blocking and returns true if computation has completed, false otherwise.

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

Future.get()is used to retrieve the result of computation.

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

You have a couple of options:

你有几个选择:

  • call isDone()and if the result is ready ask for it by invoking get(), notice how there is no blocking
  • block indefinitely with get()
  • block for specified timeout with get(long timeout, TimeUnit unit)
  • 调用isDone(),如果结果准备好通过调用请求它get(),注意没有阻塞
  • 无限期地阻止 get()
  • 阻止指定的超时时间 get(long timeout, TimeUnit unit)

The whole Future APIthing is there to have easy way obtaining values from threads executing parallel tasks. This can be done synchronously or asynchronously if you prefer, as described in bullets above.

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

UPDATE WITH CACHE EXAMPLE

使用缓存更新示例

Here is a cache implementation from Java Concurrency In Practice, an excellent use case for Future.

下面是缓存实现的Java并发实践,一个优秀的用例Future

  • If the computation is already running, caller interested in result of computation will wait for computation to finish
  • If the result is ready in the cache, caller will collect it
  • if the result is not ready and computation has not started yet, caller will start computation and wrap result in Futurefor other callers.
  • 如果计算已经在运行,对计算结果感兴趣的调用者将等待计算完成
  • 如果结果在缓存中准备好,调用者将收集它
  • 如果结果尚未准备好且计算尚未开始,则调用者将开始计算并将结果包装到Future其他调用者中。

This is all easily achieved with FutureAPI.

这一切都可以通过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());
        }
    }
  }
}

回答by ring bearer

In the example you have given you might as well run everything in your main()method and go your merry way.

在您给出的示例中,您不妨运行main()方法中的所有内容,然后尽情享受。

But let us assume you have three steps of computation that you are currently running sequentially. Just for understanding let us assume that step1 takes t1 seconds, step2 takes t2 seconds, and step3 takes t3 seconds to complete. So total computation time is t1+t2+t3. Also, let us assume that t2>t1>=t3.

但是让我们假设您当前正在按顺序运行三个计算步骤。为了便于理解,让我们假设 step1 需要 t1 秒,step2 需要 t2 秒,step3 需要 t3 秒才能完成。所以总的计算时间是t1+t2+t3。另外,让我们假设t2>t1>=t3.

Now let us consider a scenario when we executed these three steps in parallel using Futureto hold each computational results. You can check if each task is done using non-blocking isDone()call on corresponding futures. Now what happens? theoretically your execution is as fast as how t2completes right? So we didgain some benefits from parallelism.

现在让我们考虑一个场景,当我们并行执行这三个步骤时,Future用于保存每个计算结果。您可以使用isDone()对相应期货的非阻塞调用来检查每个任务是否已完成。现在会发生什么?从理论上讲,您的执行与t2完成的速度一样快吗?所以我们确实从并行性中获得了一些好处。

Also, in Java8 , there is CompletableFuturethat supports functional style call backs.

此外,在 Java8 中,CompletableFuture支持函数式风格回调。

回答by hagrawal

If you don't care about the results, then spawn a new thread and from that thread use ExectorServiceAPI for task submission. In that way, your parent thread i.e mainthread will not be blocking in any way, it would simply spawn a new thread and then will start further execution, while the new thread will submit your tasks.

如果您不关心结果,则生成一个新线程并从该线程使用ExectorServiceAPI 进行任务提交。这样,您的父线程(即main线程)不会以任何方式阻塞,它只会生成一个新线程,然后开始进一步执行,而新线程将提交您的任务。

For creating new thread - either do it yourself by having a ThreadFactoryfor your async thread creation or use some implementation of java.util.concurrent.Executor.

对于创建新线程 - 要么通过ThreadFactory为异步线程创建创建一个来自己做,要么使用java.util.concurrent.Executor.

If this is in a JEE application and you are using Spring framework then you can easily create a new asynchronous thread using @asyncannotation.

如果这是在 JEE 应用程序中并且您使用的是 Spring 框架,那么您可以使用@async注释轻松创建一个新的异步线程。

Hope this helps!

希望这可以帮助!

回答by Ravindra babu

Below is the snippet of the pseudo code. My question is- Does the below code not defeat the very notion of parallel asynchronous processing?

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

It all depends on your use case:

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

  1. If you really want to block till you get the result, use blocking get()
  2. If you can wait for a specific period to know the status instead of infinite blocking duration, use get()with time-out
  3. If you can continue without analysing the result immediately and inspect the result at future time, use CompletableFuture(java 8)

    A Future that may be explicitly completed (setting its value and status), and may be used as a CompletionStage, supporting dependent functions and actions that trigger upon its completion.

  4. You can implement callback mechanism from your Runnable/Callable. Have a look at below SE question:

    Java executors: how to be notified, without blocking, when a task completes?

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

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

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

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

回答by NiVeR

I would like to give my share on this one, more on theoretical point of view as there are some technical answers already. I would like to base my answer on the comment:

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

Let me give you my example. The tasks I submit to the service end up raising HTTP requests, The result of the HTTP request can take a lot of time. But I do need the result of each HTTP request. The tasks are submitted in a loop. If I wait for each task to return (get), then I am loosing parallelism here, ain't I?

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

which agrees with what is said in the question.

这与问题中所说的一致。

Say you have three kids, and you want to make a cake, for your birthday. Since you want to make the greatest of cakes you need a lot of different stuff to prepare it. So what you do is split the ingredients on three different lists, because where you live there exist just 3 supermarkets that sell different products, and assign each of your kids a single task, simultaneously.

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

Now, before you can start preparing the cake (let's assume again, that you need all the ingredients beforehand) you will have to wait for the kid that have to do the longest route. Now, the fact that you need to wait for all the ingredients before starting to make the cake is yournecessity, not a dependency among tasks. Your kids have been working on the tasks simoultaneously as long as they could (e.g: until the first kid completed the task). So, to conclude, here you have the paralelilsm.

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

The sequential example is described when you have 1 kid and you assign all three tasks to him/her.

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