如何子类CompletableFuture?

时间:2014-10-26 23:24:19

标签: java java-8 future

我希望继承CompletableFuture来覆盖默认的Executor。也就是说,如果用户在未指定Executor的情况下调用方法,我希望使用自己的Executor代替CompletableFuture通常使用的方法。

Javadoc暗示了子类化的可能性:

  

所有CompletionStage方法都是独立于其他公共方法实现的,因此一个方法的行为不会受到子类中其他方法的覆盖的影响。

如果底层实现依赖于像CompletableFuture.supplyAsync()这样的包私有方法,我应该如何在子类中实现像internalComplete()这样的静态方法?

如何将一个人归类为CompletableFuture?


我想做的事情......

我的用户代码需要使用相同的执行程序异步执行多个任务。例如:CompletableFuture.supplyAsync(..., executor).thenApplyAsync(..., executor).thenApplyAsync(..., executor)。我希望自定义CompletableFuture实现在所有后续调用中使用第一个执行程序。

5 个答案:

答案 0 :(得分:5)

由于您没有向我们展示您尝试过的内容,因此我们没有机会了解您的确切操作以及失败的原因。澄清后,它看起来像一个直接的装饰模式工作,不需要触及任何CompletableFuture的内部工作。

import java.util.concurrent.*;
import java.util.function.*;

public class MyCompletableFuture<T> extends CompletableFuture<T> {
    public static <T> CompletableFuture<T> supplyAsync(Supplier<T> s, Executor e) {
        return my(CompletableFuture.supplyAsync(s, e), e);
    }
    private static <T> CompletableFuture<T> my(CompletableFuture<T> f, Executor e) {
        MyCompletableFuture<T> my=new MyCompletableFuture<>(f, e);
        f.whenComplete((v,t)-> {
            if(t!=null) my.completeExceptionally(t); else my.complete(v);
        });
        return my;
    }
    private final CompletableFuture<T> baseFuture;
    private final Executor executor;

    MyCompletableFuture(CompletableFuture<T> base, Executor e) {
        baseFuture=base;
        executor=e;
    }
    private <T> CompletableFuture<T> my(CompletableFuture<T> base) {
        return my(base, executor);
    }
    @Override
    public CompletableFuture<Void> acceptEitherAsync(
            CompletionStage<? extends T> other, Consumer<? super T> action) {
        return my(baseFuture.acceptEitherAsync(other, action, executor));
    }
    @Override
    public <U> CompletableFuture<U> applyToEitherAsync(
            CompletionStage<? extends T> other, Function<? super T, U> fn) {
        return my(baseFuture.applyToEitherAsync(other, fn, executor));
    }
    @Override
    public <U> CompletableFuture<U> handleAsync(
            BiFunction<? super T, Throwable, ? extends U> fn) {
        return my(baseFuture.handleAsync(fn, executor));
    }
    @Override
    public CompletableFuture<Void> runAfterBothAsync(
            CompletionStage<?> other, Runnable action) {
        return my(baseFuture.runAfterBothAsync(other, action, executor));
    }
    @Override
    public CompletableFuture<Void> runAfterEitherAsync(
            CompletionStage<?> other, Runnable action) {
        return my(baseFuture.runAfterEitherAsync(other, action, executor));
    }
    @Override
    public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
        return my(baseFuture.thenAcceptAsync(action, executor));
    }
    @Override
    public <U> CompletableFuture<Void> thenAcceptBothAsync(
            CompletionStage<? extends U> other,
            BiConsumer<? super T, ? super U> action) {
        return my(baseFuture.thenAcceptBothAsync(other, action, executor));
    }
    @Override
    public <U> CompletableFuture<U> thenApplyAsync(
            Function<? super T, ? extends U> fn) {
        return my(baseFuture.thenApplyAsync(fn, executor));
    }
    @Override
    public <U, V> CompletableFuture<V> thenCombineAsync(
            CompletionStage<? extends U> other,
            BiFunction<? super T, ? super U, ? extends V> fn) {
        return my(baseFuture.thenCombineAsync(other, fn, executor));
    }
    @Override
    public <U> CompletableFuture<U> thenComposeAsync(
            Function<? super T, ? extends CompletionStage<U>> fn) {
        return my(baseFuture.thenComposeAsync(fn, executor));
    }
    @Override
    public CompletableFuture<Void> thenRunAsync(Runnable action) {
        return my(baseFuture.thenRunAsync(action, executor));
    }
    @Override
    public CompletableFuture<T> whenCompleteAsync(
            BiConsumer<? super T, ? super Throwable> action) {
        return my(baseFuture.whenCompleteAsync(action, executor));
    }
}

这是一个简单的测试用例,显示它按预期工作:

ScheduledExecutorService ses=Executors.newSingleThreadScheduledExecutor();
Executor e=r -> {
    System.out.println("adding delay");
    ses.schedule(r, 2, TimeUnit.SECONDS);
};
MyCompletableFuture.supplyAsync(()->"initial value", e)
  .thenApplyAsync(String::hashCode)
  .thenApplyAsync(Integer::toOctalString)
  .thenAcceptAsync(System.out::println);

答案 1 :(得分:1)

回答原始问题 - 如何将一个人归为CompletableFuture的子类?

可能从洁净室的CompletionStage接口实现(而不是具体的CompletableFuture类)开始,a是一个更好的选择。请看这里:

https://github.com/lukas-krecan/completion-stage

答案 2 :(得分:0)

据我所知,您需要自己的internalComplete()实现,在null或异常的情况下实例化您自己的AltResult。您的子类supplyAsync()应该返回新CompletableFuture子类的新实例。

答案 3 :(得分:0)

您的目标是限制对Web服务的访问速度。您的解决方案是使用引入延迟的特殊执行程序。这不是最好的解决方案,因为链中的某些功能可能会花费一些时间用于其他活动,或者只是在队列中等待太长时间处理O / S级别的处理器,因此在访问Web服务之前不需要延迟,但是无论如何,被遗嘱执行人推迟。延迟应嵌入实际执行Web服务请求的模块中。它应该记住上一个请求的时间,如果它太小,只需在通过套接字发送请求之前调用Thread.sleep()。这样你就不需要任何CompletableFuture,并且可以直接调用函数,如f3(f1(f0)))。至少它更具可读性。

答案 4 :(得分:-3)

异步编程被广泛误解。理论方法是将异步程序表示为Petri网,其中主要组件是令牌,位置和转换。从编程的角度来看,过渡应该进一步分为解雇规则和行动。逻辑链似乎太长,程序员更喜欢过度简化的方案“事件反应”,这在任何复杂情况下都不起作用。 CompletableFuture更受限制,方案是“行动 - 原始过渡 - 行动”。

在您的情况下,每个阶段必须从两个来源提供:前一阶段的结果和允许访问Web服务的资源令牌。 CompletableFuture根本不适合这种情况。我建议首先为您的案例绘制Petri网,然后使用具有同步方法的类从头开始实现基础架构。或者尝试我的异步库df4j2,它允许构建具有多个位置的转换。