在将此标记为重复之前,请仔细阅读该问题。
以下是伪代码的片段。 我的问题是 - 以下代码是否没有打败并行异步处理的概念?
我问这个的原因是因为在下面的代码中,主线程将提交要在不同线程中执行的任务。在队列中提交任务后,它会阻止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());
}
答案 0 :(得分:41)
Future
为您提供无阻塞的方法isDone()
,如果计算完成则返回true,否则返回false。
Future.get()
用于检索计算结果。
您有几个选择:
isDone()
,如果结果已准备好,请通过调用get()
来询问,请注意没有阻止get()
get(long timeout, TimeUnit unit)
整个Future API
可以轻松地从执行并行任务的线程获取值。如果您愿意,可以同步或异步完成此操作,如上面的项目符号所述。
使用CACHE示例进行更新
以下是来自 Java Concurrency In Practice 的缓存实现,这是Future
的一个很好的用例。
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());
}
}
}
}
答案 1 :(得分:8)
以下是伪代码的片段。我的问题是 - 以下代码是否没有打败并行异步处理的概念?
这完全取决于您的使用案例:
get()
get()
超时如果您可以继续而不立即分析结果并在将来检查结果,请使用CompletableFuture(java 8)
可以明确完成的未来(设置其值和状态),并且可以用作CompletionStage,支持在完成时触发的相关函数和操作。
您可以从Runnable / Callable实现回调机制。看看下面的SE问题:
Java executors: how to be notified, without blocking, when a task completes?
答案 2 :(得分:1)
在您给出的示例中,您可以使用main()
方法运行所有内容,然后以快乐的方式运行。
但是,让我们假设您正在按顺序运行三个计算步骤。为了理解,我们假设step1需要t1秒,step2需要t2秒,step3需要t3秒才能完成。因此总计算时间为t1+t2+t3
。另外,我们假设t2>t1>=t3
。
现在让我们考虑使用Future
并行执行这三个步骤来保存每个计算结果的场景。您可以使用相应期货的非阻塞isDone()
来检查每项任务是否完成。现在发生了什么?从理论上讲,你的执行速度和t2
如何完成一样快?所以我们做了从并行性中获得了一些好处。
此外,在Java8中,有CompletableFuture
支持功能样式回调。
答案 3 :(得分:1)
我想在这个问题上给出我的分享,更多的是理论观点,因为已经有一些技术答案。我想根据评论作出答案:
让我举个例子。我提交给服务的任务最终结束 提出HTTP请求,HTTP请求的结果可能需要很多 时间但我确实需要每个HTTP请求的结果。任务是 提交循环。如果我等待每个任务返回(获取),那么我 我在这里失去平行,不是吗?
与该问题中的内容一致。
假设你有三个孩子,你想为你的生日做蛋糕。既然你想制作最好的蛋糕,你需要很多不同的东西来准备它。所以你要做的就是将成分分成三个不同的清单,因为你居住的地方只有3家超市出售不同的产品,并为你的每个孩子分配一项任务simultaneously
。
现在,在你开始准备蛋糕之前(让我们再假设,你需要事先得到所有的成分),你将不得不等待必须做最长路线的孩子。现在,在开始制作蛋糕之前需要等待所有成分的事实是你的必要性,而不是任务之间的依赖性。你的孩子一直在尽可能地同时完成任务(例如:直到第一个孩子完成任务)。所以,总而言之,在这里你有了paralelilsm。
当您有1个孩子并且将所有三个任务分配给他/她时,将描述顺序示例。
答案 4 :(得分:0)
如果您不关心结果,那么会产生一个新线程,并从该线程使用ExectorService
API进行任务提交。这样,您的父线程即main
线程将不会以任何方式阻塞,它只会生成一个新线程,然后将开始进一步执行,而新线程将提交您的任务。
用于创建新线程 - 或者通过为ThreadFactory
创建异步线程或使用java.util.concurrent.Executor
的某些实现来自行完成。
如果这是在JEE应用程序中并且您正在使用Spring框架,那么您可以使用@async
注释轻松创建新的异步线程。
希望这有帮助!