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

xmq68pz9  于 2023-05-05  发布在  Java
关注(0)|答案(5)|浏览(152)

下面是伪代码的片段。下面的代码不是打败了并行异步处理的概念吗?
我问这个问题的原因是因为在下面的代码中,主线程将提交一个任务在另一个线程中执行。在队列中提交任务后,它会阻塞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());
}
wfauudbj

wfauudbj1#

Future为您提供了方法isDone(),该方法没有阻塞,如果计算完成,则返回true,否则返回false。
Future.get()用于检索计算结果。
你有几个选择:

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

整个Future API的存在是为了从执行并行任务的线程中获得值。如果您愿意,可以同步或异步完成,如上面的项目符号所述。

使用缓存示例更新

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

  • 如果计算已经在运行,对计算结果感兴趣的调用方将等待计算完成
  • 如果结果在该高速缓存中准备就绪,调用者将收集它
  • 如果结果还没有准备好并且计算还没有开始,调用者将开始计算并将结果 Package 在Future中用于其他调用者。

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

vxf3dgd42#

下面是伪代码的片段。我的问题是--下面的代码是否违背了并行异步处理的概念?
这一切都取决于你的用例:
1.如果你真的想阻塞直到得到结果,使用阻塞get()
1.如果您可以等待一段特定的时间来了解状态,而不是无限的阻塞持续时间,请使用带超时的get()
1.如果您可以继续而不立即分析结果,并在将来检查结果,请使用CompletableFuture(java 8)。
可以显式完成(设置其值和状态)的Future,并且可以用作CompletionStage,支持在其完成时触发的依赖函数和动作。
1.您可以从Runnable/Callable实现回调机制。看看下面的问题:
Java executors: how to be notified, without blocking, when a task completes?

ktecyv1j

ktecyv1j3#

我想在这一点上给予我的观点,更多的是从理论的Angular 来看,因为已经有一些技术上的答案。我想根据以下评论来回答:
让我给你举个例子。我提交给服务的任务最终会引发HTTP请求,HTTP请求的结果可能需要很多时间。但是我需要每个HTTP请求的结果。任务在循环中提交。如果我等待每个任务返回(get),那么我就失去了并行性,不是吗?
这与问题中的内容一致。
假设你有三个孩子,你想为你的生日做一个蛋糕。因为你想做最好的蛋糕,你需要很多不同的东西来准备它。所以你要做的是把成分分成三份不同的清单,因为你住的地方只有三家超市出售不同的产品,然后给你的每个孩子分配一项任务,simultaneously
现在,在你开始准备蛋糕之前(让我们再次假设,你需要事先准备好所有的成分),你必须等待必须做最长路线的孩子。现在,在开始制作蛋糕之前需要等待所有成分的事实是你的必需品,而不是任务之间的依赖性。你的孩子已经尽可能长时间地同时完成了这些任务(例如:直到第一个孩子完成任务)。所以,总而言之,这就是平行论。
当你有一个孩子,你把所有三个任务都分配给他/她时,描述了顺序的例子。

pvabu6sv

pvabu6sv4#

在你给出的例子中,你可以运行你的main()方法中的所有东西,然后继续你的快乐之路。
但是,让我们假设您有三个计算步骤,您当前正在顺序运行。为了便于理解,让我们假设步骤1需要t1秒,步骤2需要t2秒,步骤3需要t3秒完成。所以总计算时间是t1+t2+t3。假设t2>t1>=t3
现在让我们考虑一个场景,当我们使用Future来保存每个计算结果时,我们并行执行这三个步骤。您可以检查每个任务是否在相应的future上使用非阻塞isDone()调用完成。现在怎么办?理论上你的执行速度和t2一样快,对吗?所以我们确实从并行性中获得了一些好处。
另外,在Java8中,有CompletableFuture支持函数式回调。

eyh26e7m

eyh26e7m5#

如果您不关心结果,那么生成一个新线程,并从该线程使用ExectorService API进行任务提交。这样,你的父线程,即main线程,将不会以任何方式阻塞,它只会产生一个新线程,然后将开始进一步执行,而新线程将提交你的任务。
对于创建新线程--要么自己做,用一个ThreadFactory来创建异步线程,要么使用java.util.concurrent.Executor的一些实现。
如果这是在JEE应用程序中,并且您正在使用Spring框架,那么您可以使用@async注解轻松创建新的异步线程。
希望这有帮助!

相关问题