Java CompletableFuture.allOff完成,即使其列表中的一个CompletableFuture尚未完成
我有两个完整的未来。task2只能在task1完成后启动。然后,我需要等待所有任务完成。在我下面的代码中,程序在task1结束后结束。task2已启动,但未完成。你知道为什么会这样吗?另外,为什么列表只包含1个条目,而在代码中,我添加了2个条目 代码: 你有两个问题 首先,您已经创建了一个竞赛条件,以确定task2何时被添加到您的未来列表中。在执行这一行时-Java CompletableFuture.allOff完成,即使其列表中的一个CompletableFuture尚未完成,java,asynchronous,completable-future,Java,Asynchronous,Completable Future,我有两个完整的未来。task2只能在task1完成后启动。然后,我需要等待所有任务完成。在我下面的代码中,程序在task1结束后结束。task2已启动,但未完成。你知道为什么会这样吗?另外,为什么列表只包含1个条目,而在代码中,我添加了2个条目 代码: 你有两个问题 首先,您已经创建了一个竞赛条件,以确定task2何时被添加到您的未来列表中。在执行这一行时- CompletableFuture.allOf(...).get(); -我称之为终止getter,列表中只有task1。通过输出其大小
CompletableFuture.allOf(...).get();
-我称之为终止getter,列表中只有task1。通过输出其大小自己查看:
// wait for the calls to finish
try {
System.out.println("# of futures: " + futures.size()); // 1
task2最终仍会运行,因为您计划了whenComplete。但触发它的不是你的终止getter
记得我说过这是比赛条件。为了亲自演示这一点,请在终止getter之前添加一个sleep,如下所示:
try {
Thread.sleep(6000L);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// wait for the calls to finish
try {
System.out.println("# of futures: " + futures.size()); // 2
那么您就有足够的时间添加task2了
但事情是这样的。现在终止getter是否同时触发这两个任务
还是不行!这就是第二个问题:您几乎总是希望使用一个thenRun、thenAccept、thenApply和thenCompose方法。这些方法链接您的未来,即使每个阶段依赖于前一个阶段,以便您的终止getter实际等待整个链完成。whenComplete是一种特殊方法,它启动一个完全无关的管道,因此不受终止get的影响
在您的情况下,您希望使用thenRun,如下所示:
task1.thenRun( ignore -> {
好吧,那我们怎么把这些结合起来呢
public static void testFutures () throws Exception {
CompletableFuture<Void> task1 = CompletableFuture.supplyAsync( () -> {
System.out.println(" task1 start");
try {
Thread.sleep(5000L);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(" task1 done");
return null;
});
CompletableFuture<Void> futuresChain = task1.thenRun( () -> {
System.out.println(" task2 start");
try {
Thread.sleep(2000L);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(" task2 done");
});
// wait for the calls to finish
try {
futuresChain.thenRun( () -> {
System.out.println(" all tasks done ");
}).toCompletableFuture().get();
} catch (Exception e) {
e.printStackTrace();
}
}
你看,你只需要为第一个任务提供同步。您希望在该任务之后按顺序运行task2,因此run将为您安排供应同步。所以你也不需要一系列的期货。allOf用于并行运行任务,并等待所有任务完成。让我们首先清理代码 让我们定义一种睡眠方法,这样就不会弄脏水:
private static void sleep(int seconds) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
然后,让我们将任务分开,并使用适当的方法:
private static CompletableFuture<Void> task1() {
return CompletableFuture.runAsync(() -> {
System.out.println(" task1 start");
sleep(5);
System.out.println(" task1 done");
});
}
private static CompletableFuture<Void> task2() {
return CompletableFuture.runAsync(() -> {
System.out.println(" task2 start");
sleep(2);
System.out.println(" task2 done");
});
}
这对于这么简单的任务来说太复杂了。@Eugene-嗯,我的答案建立在学生目前的理解基础上,即使不完善。你的答案显示了理想的目的地。两者都很好。
task1 start
task1 done
task2 start
task2 done
all tasks done
private static void sleep(int seconds) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
private static CompletableFuture<Void> task1() {
return CompletableFuture.runAsync(() -> {
System.out.println(" task1 start");
sleep(5);
System.out.println(" task1 done");
});
}
private static CompletableFuture<Void> task2() {
return CompletableFuture.runAsync(() -> {
System.out.println(" task2 start");
sleep(2);
System.out.println(" task2 done");
});
}
public static void main(String[] args) throws Exception {
testFutures();
}
private static void testFutures() throws Exception {
CompletableFuture<Void> both = task1().thenCompose(ignoreMe -> task2());
both.get();
System.out.println("both done");
}