CompletableFuture递归可靠性

时间:2015-09-07 20:08:52

标签: java recursion functional-programming java-8 future

这是CompletableFuture的非常基本的递归,我想建立可靠的系统,所以每次重新启动过程都有异常,我相信它有太多问题,想要得到你的反馈意见

private CompletableFuture<?> recursion() {
return CompletableFuture.runAsync(() -> {
    //code here
    }).handleAsync((v, th) -> {
        if (th != null)
            return this.recursion();
        else
            return v;
    });
}

Edit1:

int tries =5;
private CompletableFuture<?> recursion() {
    return CompletableFuture.runAsync(() -> {
    //code here
    }).handleAsync((v, th) -> {
        if (th != null && tries-- > 0){
            Thread.sleep(1000);
            return this.recursion();
        }else
            return v;
    });
}

EDIT2: 清除代码,因为返回CompletableFuture<?>没有必要,因此考虑@Holger评论并使用AtomicInteger进行尝试,将其挂起void

AtomicInteger tries =5;
private void recursion() {
    CompletableFuture.runAsync(() -> {
    //code here
    }).whenCompleteAsync((v, th) -> {
        if (th != null && ( tries.getAndDecrement() > 0 ) ){
            Thread.sleep(1000);
            this.recursion();
        });
}

请将反馈发给我,我正在争论,但我真的很感激。

1 个答案:

答案 0 :(得分:0)

通常,只是在发生异常时重试操作,而不处理异常以分析失败的原因,远远没有创建可靠的系统。

然而,如果要实现重试,则代码无法正确执行此操作。

您的代码恰好被编译器接受,因为您使用的操作不会生成值并返回CompletableFuture<?>。这隐藏了代码的问题:

传递给handleAsync的双功能应该提供结果值,但是你调用this.recursion()产生CompletableFuture<?>。编译器并不介意在非例外情​​况下返回v,因为VoidCompletableFuture<?>有一个共同的超类型Object,所以整个方法是有效地返回与返回类型CompletableFuture<Object>兼容的CompletableFuture<?>

如果您将返回类型声明为CompletableFuture<Void>,则会立即识别出逻辑错误:在例外情况下,您将启动另一个异步操作,但由于您没有检查它的结果,只是返回CompletableFuture<?> ,然后将其视为Object,调用者将永远不会注意到重试(或后续重试)是否失败。来电者将始终收到报告成功的CompletableFuture<?>,其中包含(Void)nullCompletableFuture<?>作为结果值。

通常,您不应该使用递归进行重复。没有理由这样做。让我们用一个返回值的动作演示逻辑:

CompletableFuture<String> performAsyncAction() {
    Supplier<String> action=() -> {
        if(Math.random()>0.2)
            throw new IllegalStateException("simulated failure");
        return "value implying success";
    };
    int retries=5;
    return CompletableFuture.supplyAsync(() -> {
        try { return action.get(); }
        catch(Throwable t) {
            for(int i=0; i<retries; i++) try {
                Thread.sleep(1000);
                return action.get();
            } catch(Throwable next) { t.addSuppressed(next); }
            throw t;
        }
    });
}

很容易适应使用RunnablerunAsyncCompletableFuture<Void>

更新:如果您只是想在不向发起人提供反馈的情况下安排重试,您可以通过等待延迟过去来实现它而不会阻塞线程:

static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();

static void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) {
    if(tries>0)
        e.execute(()-> { try { r.run(); } catch(Throwable t) {
            e.schedule(()->performAsyncAction(r, tries-1, delay, u), delay, u);
        }});
}

这使用递归,因为它解决了lambda表达式。如果你使用内部类,那么在没有递归的情况下也可以这样做:

static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();

static void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) {
    if(tries>0)
        e.execute(new Runnable() {
            int left = tries;
            public void run() {
                try { r.run(); } catch(Throwable t) {
                    if(--left > 0) e.schedule(this, delay, u);
                }
            }
        });
}