Java 8并行运行多个方法
我有两个方法,它们有不同的返回类型,我想同时运行。这是我的密码:Java 8并行运行多个方法,java,concurrency,java-8,java.util.concurrent,completable-future,Java,Concurrency,Java 8,Java.util.concurrent,Completable Future,我有两个方法,它们有不同的返回类型,我想同时运行。这是我的密码: public void method(int id) { final CompletableFuture<List<FooA>> fooACF = CompletableFuture.supplyAsync(() -> generateFooA(id)); final CompletableFuture<List<FooB>> fooBCF = Completa
public void method(int id) {
final CompletableFuture<List<FooA>> fooACF = CompletableFuture.supplyAsync(() -> generateFooA(id));
final CompletableFuture<List<FooB>> fooBCF = CompletableFuture.supplyAsync(() -> generateFooB(id));
List<FooA> fooAs = fooACF.get();
List<FooB> fooBs = fooBCF.get();
//Do more processesing
}
public List<FooA> generateFooA(int id) {
//code
}
public List<FooB> generateFooB(int id) {
//code
}
公共无效方法(int-id){
final CompletableFuture fooACF=CompletableFuture.supplyAsync(()->generateFooA(id));
final CompletableFuture fooBCF=CompletableFuture.supplyAsync(()->generateFooB(id));
List fooAs=fooACF.get();
List fooBs=fooBCF.get();
//多加工
}
公共列表生成器OA(int id){
//代码
}
公共列表GenerateFob(int id){
//代码
}
但我不知道这两种方法是与上述代码并行运行,还是说:
List<FooA> fooAs = generateFooA(id);
List<FooB> fooBs = generateFooB(id);
List fooAs=generateFooA(id);
列表fooBs=generateFooB(id);
我如何正确地使用CompletableFutures来并行运行这两种方法?正如我在评论中所说,请查看,但这应该是您需要的
final CyclicBarrier gate = new CyclicBarrier(3);
public void method(int id) {
Thread one = new Thread (()->{
gate.await();
List<FooA> fooAs = generateFooA(id);
});
Thread two = new Thread (()->{
gate.await();
List<FooB> fooBs = generateFooB(id);
});
one.start();
two.start();
gate.await();
//Do more processesing
}
public List<FooA> generateFooA(int id) {
//code
}
public List<FooB> generateFooB(int id) {
//code
}
final CyclicBarrier gate=新的CyclicBarrier(3);
公共void方法(int-id){
线程一=新线程(()->{
门。等待();
列表fooAs=generateFooA(id);
});
线程二=新线程(()->{
门。等待();
列表fooBs=generateFooB(id);
});
一、启动();
二、启动();
门。等待();
//多加工
}
公共列表生成器OA(int id){
//代码
}
公共列表GenerateFob(int id){
//代码
}
您缺少一个执行者
:
ExecutorService executor = Executors.newCachedThreadPool();
List<Future<?>> = Stream.<Runnable>of(() -> generateFooA(id), () -> generateFooA(id))
.map(executor::submit)
.collect(Collectors.toList());
for (Future<?> future : futures) {
future.get(); // do whatever you need here
}
ExecutorService executor=Executors.newCachedThreadPool();
列出您的代码工作正常,使用ForkJoinPool.commonPool()
提供的线程,正如JavaDoc为CompletableFuture.supplyAsync(供应商)
所承诺的那样。通过添加一些sleep()
和println()
语句,您可以快速而肮脏地证明这一点。通过使用字符串
而不是列表
,我简化了您的代码:
您可以手动观察“离开”输出行在1秒和2秒后出现。要获得更多证据,可以向输出中添加时间戳。如果更改睡眠的相对长度,您将看到“离开”输出以不同的顺序出现
如果省略sleep()
s,那么第一个线程很可能会很快完成,在第二个线程开始之前就完成了:
Entering generateA Thread[ForkJoinPool.commonPool-worker-1,5,main]
Leaving generateA
Entering generateB Thread[ForkJoinPool.commonPool-worker-1,5,main]
Leaving generateB
Final fooA A1
Final fooB B1
请注意,这一切发生得太快了,以至于在运行时请求第二个线程时,线程已经返回到池中。因此,原始线程被重用
这也可能发生在睡眠时间很短的情况下,尽管在我的系统中,每次运行1毫秒的睡眠就足够了。当然,sleep()
是一个需要时间才能完成的“真实”操作的占位符。如果您的实际操作非常便宜,以至于在另一个线程启动之前就完成了,这就很好地暗示了这是一个多线程没有好处的场景
然而如果你需要问如何证明事情是同时发生的,我想知道你为什么希望事情同时发生。如果当您的程序同时或按顺序执行这些任务时,您的程序之间没有“真实世界”可观察到的差异,那么为什么不让它按顺序运行呢?更容易对顺序操作进行推理;有很多与并发相关的秘密bug
也许你希望通过多线程来提高速度——如果是这样,那么速度的提高应该是你所测量的,而不是事情是否真的是并发的。请记住,对于大量的任务,CPU不能以并行方式比顺序方式更快地执行这些任务。查看这篇文章,您的代码看起来不错。你有什么特别的担心吗?如果你问你所做的是否会在两个单独的线程上运行这两个方法,那么是的,你正确地使用了CompletableFuture
。启动另外两个线程并使用CompletableFuture
毫无意义。generateFooA(id)
调用和generateFooB(id)
调用可能正在进行中,运行在不同的线程中,在您的一个线程或两个线程到达门之前。wait()
调用。您似乎认为调用get()
正在启动任务,但任务被安排在supplyAsync
内。即使没有人调用get()
或join()
,它们也会一直运行到完成。除此之外,“完全同时”跑步根本不是一个有用的目标。即使您设法同时启动两个任务,它们也可能在下一纳秒内不同步。关于执行时间或线程调度没有任何保证。如何访问fooAs
和fooBs
并对其进行更多处理?例如,“您缺少了一个执行器
”执行器
在OP的示例中是隐含的。该示例将任务提交到ForkJoinPool.commonPool()
@James,这不是一回事。执行器尽最大努力并行执行,而这段代码独占使用它,所以并行执行是“可能的”。但是ForkJoinPool.commonPool()在所有流之间共享,因此并行执行的可能性较小(但并非不可能或理所当然)。这两种方法都“尽最大努力”并行执行,但是,即使您为每个任务创建了不同的线程,您也永远无法保证并行执行。但即使使用线程池执行器也不能保证这一点。当第一个工作线程设法在提交第二个任务之前完成第一个任务时,它将接收第二个任务(这并不重要)。如果F/J的公共池由于其他任务而没有可用容量,则意味着工作负载占用了所有cpu核心,因此提交线程可能不会在这两个任务之间运行…@Holger of c
Entering generateFooA Thread[ForkJoinPool.commonPool-worker-1,5,main]
Entering generateFooB Thread[ForkJoinPool.commonPool-worker-2,5,main]
Leaving generateFooB
Leaving generateFooA
Final fooA A1
Final fooB B1
Entering generateA Thread[ForkJoinPool.commonPool-worker-1,5,main]
Leaving generateA
Entering generateB Thread[ForkJoinPool.commonPool-worker-1,5,main]
Leaving generateB
Final fooA A1
Final fooB B1