我有两个方法,它们有不同的返回类型,我想同时运行它们。下面是我的代码:
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
}但我不知道这两种方法是否会与上面的代码并行运行,或者我只是更好地说:
List<FooA> fooAs = generateFooA(id);
List<FooB> fooBs = generateFooB(id);我如何正确地使用可完成的未来,以便能够并行运行这两种方法?
发布于 2017-11-22 00:43:03
正如CompletableFuture.supplyAsync(Supplier<U> supplier)的JavaDoc所承诺的那样,使用ForkJoinPool.commonPool()提供的线程,您的代码工作得很好。您可以通过添加一些sleep()和println()语句来快速证明这一点。我使用String而不是List<Foo>简化了您的代码
public void method(int id) throws InterruptedException, ExecutionException {
CompletableFuture<String> cfa = CompletableFuture.supplyAsync(() -> generateA(id));
CompletableFuture<String> cfb = CompletableFuture.supplyAsync(() -> generateB(id));
String fooA = cfa.get();
String fooB = cfb.get();
System.out.println("Final fooA " + fooA);
System.out.println("Final fooB " + fooB);
}
public String generateA(int id) {
System.out.println("Entering generateA " + Thread.currentThread());
sleep(2000);
System.out.println("Leaving generateA");
return "A" + id;
}
public String generateB(int id) {
System.out.println("Entering generateB " + Thread.currentThread());
sleep(1000);
System.out.println("Leaving generateB");
return "B" + id;
}
private void sleep(int n) {
try {
Thread.sleep(n);
} catch (InterruptedException ex) {
// never mind
}
}输出为:
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您可以手动观察到在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请注意,这一切发生得如此之快,以至于在运行时请求第二个线程时,线程已经返回到池中。因此,原始线程被重用。
虽然在我的系统上,每次我运行它时,1ms的睡眠时间就足够了,但这种情况也可能发生在非常短的睡眠时间内。当然,sleep()是一个需要时间才能完成的“真实”操作的占位符。如果您的实际操作非常廉价,以至于它在其他线程启动之前完成,这是一个很好的提示,表明这是一种多线程没有好处的场景。
然而如果你需要问如何证明事情是并发发生的,我想知道为什么你一开始就想让它们并发发生。如果在并发或顺序执行这些任务时,程序之间没有“真实世界”可观察到的差异,那么为什么不让它按顺序运行呢?更容易理解顺序操作;有很多与并发相关的隐蔽的bug。
也许您希望通过多线程来提高速度--如果是这样的话,速度的提高应该是您所测量的,而不是事情是否真正并发。请记住,对于非常多的任务,CPU不可能以并行方式比顺序执行得更快。
发布于 2017-11-21 05:55:02
正如我在我的评论中所说的,检查How to start two threads at "exactly" the same time,但这应该是你想要的
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
}发布于 2017-11-21 06:14:37
你丢失了一个Executor
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
}当您对它们执行submit时,Runnables就会开始执行。get()会尽快返回。例如,如果你的第一个get()是最慢的,所有其他的get()调用都会立即返回。
https://stackoverflow.com/questions/47401536
复制相似问题