【发布时间】:2017-03-08 13:49:12
【问题描述】:
我有以下代码(来自my previous question)在远程服务器上安排任务,然后使用ScheduledExecutorService#scheduleAtFixedRate 轮询完成。任务完成后,它会下载结果。
我想将Future 返回给调用者,以便他们可以决定何时阻止以及阻止多长时间,并为他们提供取消任务的选项。
我的问题是,如果客户端取消download 方法返回的Future,whenComplete 块不会执行。如果我删除 thenApply 它确实如此。很明显,我对 Future 作文有一些误解……我应该改变什么?
public Future<Object> download(Something something) {
String jobId = schedule(something);
CompletableFuture<String> job = pollForCompletion(jobId);
return job.thenApply(this::downloadResult);
}
private CompletableFuture<String> pollForCompletion(String jobId) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
CompletableFuture<String> completionFuture = new CompletableFuture<>();
ScheduledFuture<?> checkFuture = executor.scheduleAtFixedRate(() -> {
if (pollRemoteServer(jobId).equals("COMPLETE")) {
completionFuture.complete(jobId);
}
}, 0, 10, TimeUnit.SECONDS);
completionFuture
.whenComplete((result, thrown) -> {
System.out.println("XXXXXXXXXXX"); //Never happens unless thenApply is removed
checkFuture.cancel(true);
executor.shutdown();
});
return completionFuture;
}
同样,如果我这样做:
return completionFuture.whenComplete(...)
而不是
completionFuture.whenComplete(...);
return completionFuture;
whenComplete 也永远不会执行。这对我来说似乎非常违反直觉。从逻辑上讲,whenComplete 返回的Future 不应该是我应该坚持的那个吗?
编辑:
我更改了代码以明确反向传播取消。它令人憎恶且难以阅读,但它确实有效,我找不到更好的方法:
public Future<Object> download(Something something) throws ChartDataGenException, Exception {
String jobId = schedule(something);
CompletableFuture<String> job = pollForCompletion(jobId);
CompletableFuture<Object> resulting = job.thenApply(this::download);
resulting.whenComplete((result, thrown) -> {
if (resulting.isCancelled()) { //the check is not necessary, but communicates the intent better
job.cancel(true);
}
});
return resulting;
}
编辑 2:
我发现了tascalate-concurrent,这是一个出色的库,提供了CompletionStage 的合理实现,支持可以透明地反向传播取消的依赖承诺(通过DependentPromise 类)。似乎非常适合这个用例。
这就够了:
DependentPromise
.from(pollForCompletion(jobId))
.thenApply(this::download, true); //true means the cancellation should back-propagate
请注意,没有测试过这种方法。
【问题讨论】:
-
这是我的新理解: 1.在申请
whenComplete之前通过这个阶段是正确的,因为你必须取消这个阶段才能让它触发下游的动作whenComplete舞台。如果您取消whenComplete阶段,它根本不会执行。 2.您还必须坚持whenComplete阶段以确保其可达性。令人困惑... -
我已尝试根据您的代码重现您的问题(添加缺少的部分),但我没有遇到您的问题:
whenComplete()始终执行(在计划任务完成以后) ,并且客户端取消绝对没有效果——取消不会反向传播。此外,如果我实现取消的反向传播,当客户端取消未来时,whenComplete()仍会执行。你能把这段代码变成minimal reproducible example吗? -
@Didier L:我想,取消不会反向传播的事实正是 OP 必须实现的。
-
@Holger 可能确实是下一步,但这并不能解释为什么
whenComplete没有被执行 -
对于反向传播,你也可以测试
resulting.isCancelled(),而不是依赖instanceof。
标签: java multithreading concurrency java-8 completable-future