如果我有
CompletableFuture<Something> future1 = service.request(param1);
CompletableFuture<Something> future2 = service.request(param2);
CompletableFuture<Void> many = CompletableFuture.allOf(future1, future2);
当我many.cancel()
时会发生什么? future1
和future2
也会被取消吗?如果没有,最简单的方法是什么?我不愿意继续future1
和future2
,只是为了在我想取消many
时取消它们。
关于我为什么要这样做的一些背景知识:在接收一条数据时,我需要请求匹配的,可能未来的数据来执行计算。如果有更新的数据到达,我想取消先前计算的完成,因为结果将立即被新计算取代。
答案 0 :(得分:10)
在你让生活变得艰难之前,你应该知道取消CompletableFuture
实际上做了什么。最重要的是,它不停止相关的计算。
如果与CompletableFuture
关联的计算已在运行但尚未完成,则取消CompletableFuture
会将其转换为“已取消”状态,这可能对所有相关阶段产生直接影响,但不是计算,它将一直持续到完成,但它试图完成取消的未来将不会有任何影响。
虽然其他Future
可能会被中断取消,这会停止计算,如果它检查中断,这不适用于CompletableFuture
,请参阅CompletableFuture.cancel(boolean)
:< / p>
参数:
mayInterruptIfRunning - 此值在此实现中无效,因为中断不用于控制处理。
因此,当您成功取消future1
或future2
时,唯一的直接影响就是取消many
,您也可以通过调用cancel
来取消在many
本身。如果有更多的依赖阶段会产生更广泛的影响,但是既然你说过,你不想保留对future1
或future2
的引用,那么情况似乎并非如此
以下代码演示了该行为:
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
System.out.println("supplying value");
return "foo";
});
CompletableFuture<String> then = supply.thenApply(s -> {
System.out.println("Evaluating next stage");
return s;
});
CompletableFuture<?> last = then.handle((s,t) -> {
System.out.println("last stage: value: "+s+", throwable: "+t);
return "";
});
System.out.println("cancelling: "+supply.cancel(true));
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
此代码可重复打印:
last stage: value: null, throwable: java.util.concurrent.CompletionException: java.util.concurrent.CancellationException
canceling: true
supplying value
(订单可能会改变)
无论您是致电supply.cancel(true)
还是then.cancel(true)
,还是通过true
或false
;它不会停止正在进行的Supplier
评估。
如果相关计算尚未开始并且在启动时确实检查取消状态,那将会有所不同,就像CompletableFuture
中的便捷方法所产生的动作一样。这种情况很少见,通常情况下,您的service.request(paramN)
电话会触发评估。
这是CompletableFuture
的基本属性,顾名思义,它是可完成的,即任何人都可以在其上调用complete
,因此{{1}无法控制将来可能最终调用CompletableFuture
的人。因此,complete
可以实现的是将其设置为已取消状态,这意味着忽略后续完成尝试并将取消向下传播到依赖操作。
所以最重要的是,您只需在cancel
个实例上调用cancel
就行了,因为在many
和cancel
上调用future1
不太可能产生值得代码复杂化的效果。
答案 1 :(得分:4)
由CompletableFuture.allOf
构建的树不会对CompletableFuture
的给定实例进行任何引用。相反,如果只是构建完成树,那么在所有给定的CompletableFutures完成时完成(来自JavaDocs)。
因此,您可能必须保留对所有CompletableFuture
的引用,以便在需要时按顺序取消它们。
答案 2 :(得分:0)
您可以试试我的图书馆:https://github.com/vsilaev/tascalate-concurrent
它提供了真正可取消的CompletionStage实现(CompletableTask)以及组合它们的方法(Promises.all)
CompletionStage<Something> future1 = CompletableTask
.complete(param1, myExecutor).thenApplyAsync(this::serviceCall);
CompletionStage<Something> future2 = CompletableTask
.complete(param2, myExecutor).thenApplyAsync(this::serviceCall);
Promise<List<Something>> many = Promises.all(future1, future2);
现在您可以致电many.cancel(true)
,future1
和future2
将被取消(如果尚未完成)。此外,如果个别期货中的任何一个异常完成,则另一个将自动取消(再次,如果尚未完成)。