异步编程之美——CompletableFuture

本文介绍了CompletableFuture在Java中异步编程的关键操作,包括supplyAsync、thenCombine、thenCompose、applyToEither和thenApply,以及exceptionally和handle的使用场景。通过实例演示了如何有效地处理并发任务和结果的组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

supplyAsync

异步开启一个任务,并返回一个结果;入参为Supply函数式接口;

CompletableFuture busA = CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread("A公车发车");
    SmallTool.sleepMillis(3000);
    return "A公车到站";
});
SmallTool.printTimeAndThread(busA);

thenCombine

连接两个异步任务,即接收两个异步任务的结果,入参为CompletableStage和BiFunction函数式接口;
也可以等价于在调用了两个CompletableFuture的join()方法后将结果输入到BiFunction;

SmallTool.printTimeAndThread("小白下单烧鸭饭");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread("大厨斩烧鸭!");
    SmallTool.sleepMillis(3000);
    return "烧鸭ok";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread("服务员煮米饭");
    SmallTool.sleepMillis(2000);
    return "米饭ok";
}), (d, r) -> {
    SmallTool.printTimeAndThread(d);
    SmallTool.printTimeAndThread(r);
    return "服务员盛饭";
});
SmallTool.printTimeAndThread("小白打游戏");
SmallTool.printTimeAndThread(completableFuture.join());
SmallTool.printTimeAndThread("小白吃烧鸭饭");

执行结果

thenCompose

用于串联两个异步任务,即在前一个任务结束后将其结果作为入参传入Function,Function内部再启动一个CompletableFuture并执行;输入形参是
Function<? super T, ? extends CompletionStage>;

SmallTool.printTimeAndThread("小白到餐厅点餐,点了米饭 + 烧鸭! ");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread("大厨砍烧鸭 ");
    SmallTool.sleepMillis(3000);
    return "大厨砍完鸭";
}).thenCompose((res) -> CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread(res);
    SmallTool.printTimeAndThread("服务员盛饭!");
    SmallTool.sleepMillis(1000);
    return "上菜";
}));
SmallTool.printTimeAndThread("小白打游戏");
SmallTool.printTimeAndThread(String.format("%s, 小白吃饭", completableFuture.join()));

执行结果

applyToEither

用于处理当两个线程都在执行时,不管哪个先执行完,只要有一个返回就触发另一个CompletableStage的执行

CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread("A公车发车");
    SmallTool.sleepMillis(3000);
    return "A公车到站";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("B公车发车");
            SmallTool.sleepMillis(2000);
            return "B公车到站";
        }), bus -> bus
).thenCompose(bus -> CompletableFuture.runAsync(() -> {
    SmallTool.printTimeAndThread(String.format("%s , 小白上车", bus));
})).join();

运行结果

thenApply

用于将上次的结果作为当前本次操作的输入参数,该方法接收形参是Function<U,V>;有别于thenCompose的Function<U, V extends CompletableStage>;

CompletableFuture.supplyAsync(() -> {
    SmallTool.printTimeAndThread("项目经历开始讲解需求");
    SmallTool.sleepMillis(2000);
    return "讲解完毕";
}).thenApply((preRes) -> {
    SmallTool.printTimeAndThread(String.format("项目经理 %s, 程序员开始编码", preRes));
    SmallTool.sleepMillis(3000);
    return "编码完毕";
}).thenCompose((preRes) -> CompletableFuture.runAsync(() ->{
    SmallTool.printTimeAndThread(preRes);
    SmallTool.printTimeAndThread("打包构建");
    SmallTool.sleepMillis(2000);
    SmallTool.printTimeAndThread("上测试环境");
})).thenRun(() -> {
    SmallTool.printTimeAndThread("测试小哥开始测试");
    SmallTool.sleepMillis(1000);
    SmallTool.printTimeAndThread("无发现bug");
    SmallTool.printTimeAndThread("上线");
}).join();

运行结果

exceptionally

用于获取异步任务的异常结果并进行处理;
入参是Function<Throwable, ? extends T>函数式接口;

CompletableFuture.runAsync(() -> {
    SmallTool.printTimeAndThread("出bug 出bug 出bug");
    SmallTool.sleepMillis(3000);
    throw new RuntimeException("bug");
}).exceptionally((e) -> {
    e.printStackTrace();
    return null;
}).join();

运行结果

handle

不管是正常还是异常结束,当执行完毕时会调用该方法;
入参是BiFunction<? super T, Throwable, ? extends U>,返回一个CompletableFuture;

CompletableFuture<String> join = CompletableFuture.supplyAsync(() -> {
    SmallTool.sleepMillis(1000);
    return "avc";
}).handle((res, e) -> {
    return CompletableFuture.supplyAsync(() -> {
        SmallTool.printTimeAndThread(res);
        e.printStackTrace();
        return "bvc";
    });
}).join();
System.out.println(join);
### CompletableFuture与响应式编程的关系 CompletableFutureJava 8 引的一种用于异步编程的强大工具,其设计目标之一是为了简化多阶段异步任务的编排和组合[^1]。尽管它本身并不是严格意义上的响应式编程框架,但它确实具备一些类似于响应式编程的核心特性,比如基于流的数据处理、非阻塞操作以及事件驱动机制。 #### 响应式编程简介 响应式编程是一种面向数据流和变化传播的编程范式,强调通过声明式的风格来描述数据流动的过程。它的核心理念在于当某个输发生变化时,整个系统的状态会自动更新以反映这些变化。常见的响应式编程库有 RxJava 和 Project Reactor,在这些框架中,开发者可以构建复杂的反应链条,从而实现高效的异步和并行计算[^2]。 #### CompletableFuture 的特点及其与响应式编程的区别 虽然 CompletableFutures 提供了许多功能使其接近于响应式编程模型,但两者之间仍然存在显著差异: - **背压支持**:真正的响应式系统通常具有内置的支持来应对生产者过快的情况(即所谓的“背压”)。然而,标准版的 `CompletableFuture` 不提供这种能力;如果上游生成的速度远超下游消费能力,则可能导致内存溢出等问题[^3]。 - **不可变性和纯度**:RxJava 或 Reactive Streams 更倾向于采用不可变对象的设计原则,这有助于减少副作用并提高程序稳定性。相比之下,`CompletableFuture` 可能涉及更多可变的状态管理[^4]。 #### 使用方法对比 以下是两种方式下如何完成相同逻辑的例子——假设我们需要先发起一次网络请求获取用户信息,再根据该用户的偏好加载个性化内容列表: ##### 利用 CompletableFuture 实现 ```java public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> fetchUser(), executor); CompletableFuture<List<String>> contentListFuture = userFuture.thenApply(user -> loadContentForUser(user)); contentListFuture.whenComplete((result, throwable) -> { if (throwable != null){ System.out.println("Error occurred: "+throwable.getMessage()); }else{ result.forEach(System.out::println); } }); executor.shutdown(); } private static String fetchUser(){ try { Thread.sleep(100); } catch(Exception e){} return "John Doe"; } private static List<String> loadContentForUser(String userName){ try { Thread.sleep(50); } catch(Exception e){} return Arrays.asList(userName+" likes sports",userName+" enjoys reading"); } ``` ##### 使用 RxJava 表达同样的流程 ```java Observable.just(fetchUser()) .flatMap(user-> Observable.fromIterable(loadContentForUser(user))) .subscribe(contentItem->{System.out.println(contentItem);}, error->{error.printStackTrace();}); ``` 上述例子展示了两者的语法糖不同之处,其中 RxJava 显得更加简洁流畅。 ### 联系点分析 即便如此,二者并非完全对立的概念。实际上,许多实际项目里可能会混合运用这两种技术栈的优势部分。例如,可以在某些特定场景下调用 completable futures 来补充缺乏的功能模块,同时整体架构依旧遵循响应式设计理念。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值