CompletableFuture#complete 还能这么玩

一、一些启发

最近看了一些中间件框架,经常使用到 CompletableFuture 这个工具类。其中 complete(T value) 比较有意思,通过这个接口,可以给我们异步编程一些启发。

complete方法用于设置CompletableFuture的结果值,并标记它为完成状态。如果CompletableFuture之前没有完成,这个方法会改变它的状态,并返回true;如果已经完成,则返回false。这个方法是线程安全的,可以被多个线程调用。

Java 复制代码
/**
 * If not already completed, sets the value returned by {@link
 * #get()} and related methods to the given value.
 *
 * @param value the result value
 * @return {@code true} if this invocation caused this CompletableFuture
 * to transition to a completed state, else {@code false}
 */
public boolean complete(T value) {
    boolean triggered = completeValue(value);
    postComplete();
    return triggered;
}

CompletableFuture#complete(T value) 方法的主要功能是手动设置 CompletableFuture 的计算结果。根据某些条件控制异步任务的完成或者模拟异步操作时非常有用,在一些 RPC 中有用到。

二、通过一个案例来理解 complete 用法

下面是一个使用 CompletableFuture#complete(T value) 的实际案例,这个例子模拟了一个简单的异步任务,其中主线程启动一个异步任务来计算某个结果,并在计算完成后,手动通过 complete 方法设置结果。

Java 复制代码
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureCompleteExample {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建一个线程池来执行异步任务
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 创建一个CompletableFuture对象
        CompletableFuture<String> future = new CompletableFuture<>();

        // 异步任务:计算结果
        executor.submit(() -> {
            try {
                // 模拟长时间运行的任务
                Thread.sleep(2000);
                String result = "Computed Result";
                // 计算完成后,使用complete方法设置结果
                future.complete(result);
            } catch (InterruptedException e) {
                future.completeExceptionally(e);
            }
        });

        // 等待异步任务完成,并获取结果
        String result = future.get();
        System.out.println("Result: " + result);

        // 关闭线程池
        executor.shutdown();
    }
}

这个异步任务在模拟了 3 秒的计算时间后,通过调用 future.complete("Computed Result") 手动完成了 CompletableFuture,并设置了计算结果。主线程通过调用 future.get() 阻塞等待异步任务的结果。一旦异步任务完成,get() 方法将返回结果。

这个特性非常适合异步编程中结果的封装。接下来看一些 RPC 框架都是怎么玩的。

三、最佳实践

guide-rpc-framework

github.com/Snailclimb/...

路径:github.javaguide.remoting.transport.netty.client.UnprocessedRequests#complete

具体调用过程如下:

客户端发送请求后,get() 将一直阻塞,直到客户端收到服务端的返回结果后调用 complete 结束。

关键几步如下:

在发送之前创建 CompletableFuture 给到发送者,使之持有 CompletableFuture 的引用

get() 阻塞获取结果

在完成数据请求后,回调 complete,并填充结果。

通过上面几步高效的获取到了异步编程的结果。

dubbo 3.0 版本

github.com/apache/dubb...

具体类位置:org.apache.dubbo.remoting.exchange.support.DefaultFuture#doReceived

DefaultFuture 的类型为 Public class DefaultFuture extends CompletableFuture

Java 复制代码
private void doReceived(Response res) {
    if (res == null) {
        throw new IllegalStateException("response cannot be null");
    }
    if (res.getStatus() == Response.OK) {
        this.complete(res.getResult());
    } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
        this.completeExceptionally(
                new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()));
    } else if (res.getStatus() == Response.SERIALIZATION_ERROR) {
        this.completeExceptionally(new SerializationException(res.getErrorMessage()));
    } else {
        this.completeExceptionally(new RemotingException(channel, res.getErrorMessage()));
    }
}

通过 netty 调用成功后,将结果设置到 DefaultFuture 中,完成此次异步请求。

上面的方式基本算是一个模版套路了,可以模仿写一个类似的功能。

四、最后

如果异步任务中发生异常,通过 future.completeExceptionally(e) 来标记 CompletableFuture 为异常完成状态,这样调用 get() 方法时会抛出 ExecutionException,其原因为原始异常。

对于异步编程,想获取返回结果的情况,可以考虑采用 CompletableFuture#complete 方法的方式,还算灵活。

相关推荐
葫芦和十三5 小时前
图解 MongoDB 26|片键设计:决定集群命运的一个决定
后端·mongodb·agent
Avan_菜菜6 小时前
使用 Docker + rclone 自建 WebDAV
后端·agent·claude
小bo波7 小时前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
阳光是sunny7 小时前
别再被 worktree 绕晕了!AI 编程时代你必须掌握的 Git 隔离神器
前端·人工智能·后端
万少9 小时前
万少的博客 - 技术分享与解决方案
前端·javascript·后端
咖啡八杯9 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
苍何9 小时前
腾讯再放大招,企微 Agent 大圆开启内测
后端
ethantan9 小时前
一篇讲解AI Agent 组成:像人一样思考的智能体
人工智能·后端·程序员
Cosolar11 小时前
vLLM 生产级部署完全指南
人工智能·后端·架构
IT_陈寒12 小时前
垃圾回收器选错了,我的Java服务内存炸了
前端·人工智能·后端