递归取消allOff CompletableFuture

如果我有

CompletableFuture future1 = service.request(param1); CompletableFuture future2 = service.request(param2); CompletableFuture many = CompletableFuture.allOf(future1, future2); 

当我做many.cancel()时会发生什么? future1future2被取消吗? 如果没有,那么实现这一目标的最简洁方法是什么? 我不愿意坚持future1future2 ,只是为了能够在取消many时取消它们。

关于为什么我想要这个的一些背景:当接收一条数据时,我需要请求匹配的,可能未来的数据来执行计算。 如果有更新的数据到达,我想取消先前计算的完成,因为结果将立即被新计算取代。

在你让生活变得艰难之前,你应该知道取消CompletableFuture实际上做了什么。 最重要的是,它不会停止相关的计算。

如果与CompletableFuture相关联的计算已经运行但尚未完成,则取消CompletableFuture会将其转换为“已取消”状态,这可能对所有相关阶段产生直接影响,但不会影响计算,这将持续到完成,虽然它试图完成取消的未来将不会有任何影响。

虽然其他Future可能会被中断取消,这将停止计算,如果它检查中断,这不适用于CompletableFuture ,请参阅CompletableFuture.cancel(boolean)

参数:

mayInterruptIfRunning – 此值在此实现中无效,因为中断不用于控制处理。

因此,当您成功取消future1future2 ,唯一的直接影响是取消many ,您也可以通过调用many本身cancel来实现。 如果有更多的依赖阶段会产生更广泛的影响,但是既然你说过,你不想继续引用future1future2 ,那么情况似乎并非如此。

以下代码演示了该行为:

 CompletableFuture supply = CompletableFuture.supplyAsync(() -> { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); System.out.println("supplying value"); return "foo"; }); CompletableFuture 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)还是调用truefalse ; 它不会阻止正在进行的Supplier评估。

如果相关计算尚未开始并且它在启动时检查取消状态,则会有所不同,就像CompletableFuture的便捷方法所产生的操作一样。 这是一种罕见的情况,通常,您的service.request(paramN)调用应该触发评估。

正如其名称所示,它是CompletableFuture的基本属性,它是可complete ,即任何人都可以在其上调用complete ,因此, CompletableFuture无法控制将来可能最终调用complete的人。 因此, cancel可以实现的是将其设置为取消状态,这意味着忽略后续完成尝试并将取消向下传播到依赖操作。


所以最重要的是你可能已经很好,只需在many实例上调用cancel ,因为在future1future2上调用cancel不太可能产生值得代码复杂化的效果。

CompletableFuture.allOf构造的树不包含对CompletableFuture的给定实例的任何引用。 相反,如果只是构建完成树,这是在所有给定的CompletableFutures完成时完成的 (来自JavaDocs )。

因此,您可能必须保留对所有CompletableFuture引用,以便在需要时按顺序取消它们。

您可以尝试我的库: https : //github.com/vsilaev/tascalate-concurrent

它提供了真正可取消的CompletionStage实现(CompletableTask)以及组合它们的方法(Promises.all)

 CompletionStage future1 = CompletableTask .complete(param1, myExecutor).thenApplyAsync(this::serviceCall); CompletionStage future2 = CompletableTask .complete(param2, myExecutor).thenApplyAsync(this::serviceCall); Promise> many = Promises.all(future1, future2); 

现在你可以调用many.cancel(true)future1future2都将被取消(如果还没有完成)。 此外,如果个别期货中的任何一个exception完成,则另一个将自动取消(再次,如果尚未完成)。