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