异步编程——CompletableFuture

CompletableFuture 是 Java 8 引入的一个强大的异步编程工具,位于 java.util.concurrent 包中。它是 Future 接口的实现类,但相比传统的 FutureCompletableFuture 提供了更灵活、更强大的异步任务处理能力,支持链式调用、组合多个异步任务、异常处理等功能,极大地简化了 Java 中异步编程的复杂性。

一、CompletableFuture 的作用

CompletableFuture 主要用于:

  • ​异步执行任务​​:可以在另一个线程中执行耗时操作,而不会阻塞当前线程。
  • ​任务组合​​:可以将多个异步任务组合起来,比如:一个任务依赖另一个任务的结果、多个任务并行执行后合并结果等。
  • ​异常处理​​:提供了丰富的 API 来处理异步任务中可能发生的异常。
  • ​回调机制​​:支持在任务完成后执行回调操作,无需手动阻塞等待结果。

 这里主要介绍第一种用法(最近优化接口响应速度用到了这个方法,其他暂时没有用过,等以后再补充):异步执行任务

二、用法

异步执行​​可以通过静态方法(如 supplyAsync 和 runAsync)将任务提交到线程池中异步执行。

  • CompletableFuture.supplyAsync(Supplier<U> supplier)
    异步执行一个有返回值的任务(Supplier),返回一个 CompletableFuture<U>

  • CompletableFuture.runAsync(Runnable runnable)
    异步执行一个没有返回值的任务(Runnable),返回一个 CompletableFuture<Void>

 默认情况下,supplyAsync 和 runAsync 使用的是 ForkJoinPool.commonPool() 作为线程池。如果需要指定线程池,可以传入第二个参数,例如:CompletableFuture.supplyAsync(() -> ..., executor);

三、普通顺序执行和CompletableFuture 异步执行对比

顺序执行:

public class SyncExample {

    // 模拟从服务获取用户信息
    public static String getUserInfo() {
        try {
            // 模拟网络延迟
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "UserInfo";
    }

    // 模拟从服务获取订单信息
    public static String getOrderInfo() {
        try {
            // 模拟网络延迟
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "OrderInfo";
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis(); // 记录开始时间

        // 依次调用两个服务
        String userInfo = getUserInfo(); // 耗时 2 秒
        String orderInfo = getOrderInfo(); // 耗时 3 秒

        // 合并结果
        String result = userInfo + " + " + orderInfo;

        long endTime = System.currentTimeMillis(); // 记录结束时间
        System.out.println("同步方式结果: " + result);
        System.out.println("同步方式耗时: " + (endTime - startTime) + " 毫秒");
    }
}

执行结果:

CompletableFuture 异步执行:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class AsyncExample {

    // 模拟从服务获取用户信息
    public static String getUserInfo() {
        try {
            // 模拟网络延迟
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "UserInfo";
    }

    // 模拟从服务获取订单信息
    public static String getOrderInfo() {
        try {
            // 模拟网络延迟
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "OrderInfo";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long startTime = System.currentTimeMillis(); // 记录开始时间

        // 异步调用 getUserInfo()
        CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo());

        // CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo(),executor);可以指定线程池

        // 异步调用 getOrderInfo()
        CompletableFuture<String> orderFuture = CompletableFuture.supplyAsync(() -> getOrderInfo());

        // 等待两个任务都完成,并合并结果
        CompletableFuture<String> combinedFuture = userFuture.thenCombine(orderFuture, (userInfo, orderInfo) -> {
            return userInfo + " + " + orderInfo;
        });

        // 获取最终结果(阻塞直到任务完成)
        String result = combinedFuture.get();

        long endTime = System.currentTimeMillis(); // 记录结束时间
        System.out.println("异步方式结果: " + result);
        System.out.println("异步方式耗时: " + (endTime - startTime) + " 毫秒");
    }
}

执行结果

   getUserInfo() 和 getOrderInfo() 是异步执行的,它们会分别在不同的线程中运行,互不阻塞。程序会同时发起这两个任务的调用,而不是等待一个完成后再开始另一个。由于两个任务是并行执行的,总耗时取决于​​耗时最长的那个任务​​,即 3 秒

可以指定自定义的线程池:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

ExecutorService executor = Executors.newFixedThreadPool(2); // 创建一个固定大小的线程池

CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo(), executor);
CompletableFuture<String> orderFuture = CompletableFuture.supplyAsync(() -> getOrderInfo(), executor);

### 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、付费专栏及课程。

余额充值