CompletableFuture 递归确保可靠性

Bas*_*hdy 5 java recursion functional-programming future java-8

这是非常基本的递归CompletableFuture,我想建立可靠的系统,所以每次遇到异常都要重新启动进程,我相信它有太多问题,并且希望得到您的反馈

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

编辑1:

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;
    });
}
Run Code Online (Sandbox Code Playgroud)

Edit2:清理代码,因为返回 CompletableFuture<?>不需要,所以void考虑到 @Holger 注释并使用 AtomicInteger 进行尝试,将其挂起以返回

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

请向我发送您的反馈,我正在争论,但真的很感激。

Hol*_*ger 0

一般来说,仅仅在发生异常时重试操作,而不处理异常并分析失败的原因,距离创建可靠的系统还很远。

\n\n

然而,如果您想实现重试,您的代码将无法正确执行此操作。

\n\n

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

\n\n

传递给的双函数handleAsync应该提供结果值,但您正在调用this.recursion()它会产生一个CompletableFuture<?>. 编译器不介意在非例外情​​况下v返回,因为VoidCompletableFuture<?>具有共同的超类型 ,Object因此整个方法实际上返回CompletableFuture<Object>与返回类型 兼容的a CompletableFuture<?>

\n\n

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

\n\n

一般来说,您不应该\xe2\x80\x99 使用递归进行重复。没有理由这样做。让\xe2\x80\x99s 通过返回值的操作来演示逻辑:

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

它\xe2\x80\x99s很容易适应使用Runnable,runAsyncCompletableFuture<Void>

\n\n
\n\n

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

\n\n
static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();\n\nstatic void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) {\n    if(tries>0)\n        e.execute(()-> { try { r.run(); } catch(Throwable t) {\n            e.schedule(()->performAsyncAction(r, tries-1, delay, u), delay, u);\n        }});\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这使用了递归,因为它决定了 lambda 表达式。如果您使用内部类,则无需递归即可实现相同的效果:

\n\n
static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();\n\nstatic void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) {\n    if(tries>0)\n        e.execute(new Runnable() {\n            int left = tries;\n            public void run() {\n                try { r.run(); } catch(Throwable t) {\n                    if(--left > 0) e.schedule(this, delay, u);\n                }\n            }\n        });\n}\n
Run Code Online (Sandbox Code Playgroud)\n