Java 8 CompletableFuture惰性计算控制
Java 8 CompletableFuture lazy computation control
我有一个关于 CompletableFuture 及其在延迟计算中的可能用途的问题。
它似乎是此任务 RunnableFuture
的一个很好的替代品,因为它可以轻松创建任务链并完全控制每个链 link。我仍然发现很难控制计算的确切时间。
如果我只是用 supplyAssync
方法或类似方法创建一个 CompletableFuture
,就可以了。它耐心地等待我调用 get
或 join
方法来计算。但是,如果我尝试使用 whenCompose
、handle
或任何其他方法创建一个实际的链,评估会立即开始,这非常令人沮丧。
当然,我总是可以在链的开头放置一些阻塞任务,并在我准备好开始计算时释放块,但这似乎是一个有点难看的解决方案。有谁知道如何控制 CompletableFuture
实际上 运行 的时间。
RunnableFuture
和 CompletableFuture
之间存在概念上的差异,您在这里忽略了这一点。
RunnableFuture
实现将任务作为输入并坚持下去。它在您调用 run
方法时运行任务。
- A
CompletableFuture
没有坚持任务。它只知道任务的结果。它有三种状态:完成、未完成和异常完成(失败)。
CompletableFuture.supplyAsync
是一个工厂方法,给你一个不完整的CompletableFuture
。它还安排了一个任务,当它完成时,会将其结果传递给 CompletableFuture
的 complete
方法。换句话说,supplyAsync
交给你的未来对任务一无所知,也无法控制任务何时运行。
要按照您描述的方式使用 CompletableFuture
,您需要创建一个子类:
public class RunnableCompletableFuture<T> extends CompletableFuture<T> implements RunnableFuture<T> {
private final Callable<T> task;
public RunnableCompletableFuture(Callable<T> task) {
this.task = task;
}
@Override
public void run() {
try {
complete(task.call());
} catch (Exception e) {
completeExceptionally(e);
}
}
}
处理问题的一种简单方法是将 CompletableFuture 包装在具有惰性的东西中。您可以使用 Supplier 甚至 Java 8 Stream。
CompletableFuture 是一种推送式设计,即一旦结果可用,就会将结果推送到相关任务。这也意味着本身未被消耗的侧链仍会被执行,这可能会产生副作用。
您想要的是一种拉式设计,其中祖先只会在其数据被消耗时被拉入。
这将是一个根本不同的设计,因为永远不会发生未消耗树的副作用。
当然,如果有足够的扭曲,CF 可以做你想做的事,但你应该研究 fork-join 框架,它只允许你 运行 你依赖的计算而不是下推结果。
已经晚了,但是如何为链中的第一个 CompletableFuture 使用构造函数?
CompletableFuture<Object> cf = new CompletableFuture<>();
// compose the chain
cf.thenCompose(sometask_here);
// later starts the chain with
cf.complete(anInputObject);
我有一个关于 CompletableFuture 及其在延迟计算中的可能用途的问题。
它似乎是此任务 RunnableFuture
的一个很好的替代品,因为它可以轻松创建任务链并完全控制每个链 link。我仍然发现很难控制计算的确切时间。
如果我只是用 supplyAssync
方法或类似方法创建一个 CompletableFuture
,就可以了。它耐心地等待我调用 get
或 join
方法来计算。但是,如果我尝试使用 whenCompose
、handle
或任何其他方法创建一个实际的链,评估会立即开始,这非常令人沮丧。
当然,我总是可以在链的开头放置一些阻塞任务,并在我准备好开始计算时释放块,但这似乎是一个有点难看的解决方案。有谁知道如何控制 CompletableFuture
实际上 运行 的时间。
RunnableFuture
和 CompletableFuture
之间存在概念上的差异,您在这里忽略了这一点。
RunnableFuture
实现将任务作为输入并坚持下去。它在您调用run
方法时运行任务。- A
CompletableFuture
没有坚持任务。它只知道任务的结果。它有三种状态:完成、未完成和异常完成(失败)。
CompletableFuture.supplyAsync
是一个工厂方法,给你一个不完整的CompletableFuture
。它还安排了一个任务,当它完成时,会将其结果传递给 CompletableFuture
的 complete
方法。换句话说,supplyAsync
交给你的未来对任务一无所知,也无法控制任务何时运行。
要按照您描述的方式使用 CompletableFuture
,您需要创建一个子类:
public class RunnableCompletableFuture<T> extends CompletableFuture<T> implements RunnableFuture<T> {
private final Callable<T> task;
public RunnableCompletableFuture(Callable<T> task) {
this.task = task;
}
@Override
public void run() {
try {
complete(task.call());
} catch (Exception e) {
completeExceptionally(e);
}
}
}
处理问题的一种简单方法是将 CompletableFuture 包装在具有惰性的东西中。您可以使用 Supplier 甚至 Java 8 Stream。
CompletableFuture 是一种推送式设计,即一旦结果可用,就会将结果推送到相关任务。这也意味着本身未被消耗的侧链仍会被执行,这可能会产生副作用。
您想要的是一种拉式设计,其中祖先只会在其数据被消耗时被拉入。 这将是一个根本不同的设计,因为永远不会发生未消耗树的副作用。
当然,如果有足够的扭曲,CF 可以做你想做的事,但你应该研究 fork-join 框架,它只允许你 运行 你依赖的计算而不是下推结果。
已经晚了,但是如何为链中的第一个 CompletableFuture 使用构造函数?
CompletableFuture<Object> cf = new CompletableFuture<>();
// compose the chain
cf.thenCompose(sometask_here);
// later starts the chain with
cf.complete(anInputObject);