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

时间:2022-02-16 00:27:43

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());
}

5 个解决方案

#1


32  

Future offers 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
  • 调用isDone(),如果结果准备好,可以通过调用get()来询问它,注意没有阻塞

  • block indefinitely with get()
  • 用get()无限地阻塞

  • block for specified timeout with get(long timeout, TimeUnit unit)
  • 使用get(长超时,TimeUnit单位)阻止指定的超时

The whole Future API thing 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

使用CACHE示例进行更新

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

这是来自Java Concurrency In Practice的缓存实现,这是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 Future for other callers.
  • 如果结果尚未就绪且计算尚未开始,则调用者将开始计算并将结果包装在Future中以供其他调用者使用。

This is all easily achieved with Future API.

使用Future API可以很容易地实现这一点。

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


6  

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. 如果你真的想阻止直到你得到结果,使用阻塞get()

  3. If you can wait for a specific period to know the status instead of infinite blocking duration, use get() with time-out
  4. 如果您可以等待特定时间段来了解状态而不是无限阻塞持续时间,请使用带超时的get()

  5. If you can continue without analysing the result immediately and inspect the result at future time, use CompletableFuture (java 8)

    如果您可以继续而不立即分析结果并在将来检查结果,请使用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.

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

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

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

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

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

#3


1  

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 Future to 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 t2 completes right? So we did gain some benefits from parallelism.

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

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

此外,在Java8中,有CompletableFuture支持功能样式回调。

#4


1  

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请求的结果。任务以循环方式提交。如果我等待每个任务返回(获取),那么我在这里失去并行性,不是吗?

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家超市销售不同的产品,并同时为你的每个孩子分配一项任务。

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 your necessity, 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.

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

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

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

#5


0  

If you don't care about the results, then spawn a new thread and from that thread use ExectorService API for task submission. In that way, your parent thread i.e main thread 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.

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

For creating new thread - either do it yourself by having a ThreadFactory for 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 @async annotation.

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

Hope this helps!

希望这可以帮助!

#1


32  

Future offers 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
  • 调用isDone(),如果结果准备好,可以通过调用get()来询问它,注意没有阻塞

  • block indefinitely with get()
  • 用get()无限地阻塞

  • block for specified timeout with get(long timeout, TimeUnit unit)
  • 使用get(长超时,TimeUnit单位)阻止指定的超时

The whole Future API thing 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

使用CACHE示例进行更新

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

这是来自Java Concurrency In Practice的缓存实现,这是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 Future for other callers.
  • 如果结果尚未就绪且计算尚未开始,则调用者将开始计算并将结果包装在Future中以供其他调用者使用。

This is all easily achieved with Future API.

使用Future API可以很容易地实现这一点。

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


6  

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. 如果你真的想阻止直到你得到结果,使用阻塞get()

  3. If you can wait for a specific period to know the status instead of infinite blocking duration, use get() with time-out
  4. 如果您可以等待特定时间段来了解状态而不是无限阻塞持续时间,请使用带超时的get()

  5. If you can continue without analysing the result immediately and inspect the result at future time, use CompletableFuture (java 8)

    如果您可以继续而不立即分析结果并在将来检查结果,请使用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.

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

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

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

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

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

#3


1  

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 Future to 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 t2 completes right? So we did gain some benefits from parallelism.

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

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

此外,在Java8中,有CompletableFuture支持功能样式回调。

#4


1  

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请求的结果。任务以循环方式提交。如果我等待每个任务返回(获取),那么我在这里失去并行性,不是吗?

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家超市销售不同的产品,并同时为你的每个孩子分配一项任务。

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 your necessity, 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.

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

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

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

#5


0  

If you don't care about the results, then spawn a new thread and from that thread use ExectorService API for task submission. In that way, your parent thread i.e main thread 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.

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

For creating new thread - either do it yourself by having a ThreadFactory for 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 @async annotation.

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

Hope this helps!

希望这可以帮助!