【多线程编程】

CompletableFuture

以下是使用 Java 中 CompletableFuture 实现四个线程顺序执行的代码示例,模拟叫车业务的四个步骤:

java 复制代码
import java.util.concurrent.CompletableFuture;

public class ThreadOrderExecutionDemo {
    // 线程A:检查用户定位是否有效
    public static CompletableFuture<Boolean> checkLocationValid() {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("线程A:检查用户定位...");
            // 模拟业务逻辑,这里假设定位有效
            return true;
        });
    }

    // 线程B:匹配附近司机
    public static CompletableFuture<String> matchDriver(Boolean isLocationValid) {
        return CompletableFuture.supplyAsync(() -> {
            if (!isLocationValid) {
                throw new RuntimeException("定位无效,无法匹配司机");
            }
            System.out.println("线程B:匹配附近司机...");
            // 模拟匹配到司机,返回司机ID
            return "司机ID_123";
        });
    }

    // 线程C:发送订单请求给司机
    public static CompletableFuture<Boolean> sendOrderRequest(String driverId) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("线程C:向司机 " + driverId + " 发送订单请求...");
            // 模拟发送成功
            return true;
        });
    }

    // 线程D:更新客户端已匹配司机状态
    public static CompletableFuture<Void> updateClientStatus(Boolean isOrderSent) {
        return CompletableFuture.runAsync(() -> {
            if (isOrderSent) {
                System.out.println("线程D:更新客户端为已匹配司机状态");
            } else {
                System.out.println("线程D:订单发送失败,客户端状态未更新");
            }
        });
    }

    public static void main(String[] args) {
        CompletableFuture<Void> result = checkLocationValid()
                .thenCompose(ThreadOrderExecutionDemo::matchDriver)
                .thenCompose(ThreadOrderExecutionDemo::sendOrderRequest)
                .thenCompose(ThreadOrderExecutionDemo::updateClientStatus);

        // 等待所有流程执行完成
        result.join();
        System.out.println("叫车业务流程执行完成");
    }
}

代码说明

  • checkLocationValid 方法:模拟检查用户定位的线程A,返回定位是否有效。
  • matchDriver 方法:模拟匹配司机的线程B,依赖线程A的定位结果,若定位无效则抛出异常。
  • sendOrderRequest 方法:模拟发送订单的线程C,依赖线程B的司机匹配结果。
  • updateClientStatus 方法:模拟更新客户端状态的线程D,依赖线程C的订单发送结果。
  • main 方法 :通过 CompletableFuturethenCompose 方法实现线程的顺序编排,确保一个线程执行完成后再执行下一个线程,同时保持整体的并发能力(其他业务可并行处理)。

核心定位、常用方法分类、实战示例三个维度梳理。

CompletableFuture是Java 8为解决传统Future只能阻塞获取结果、无法异步回调的痛点设计的异步编程工具,它实现了FutureCompletionStage接口,支持链式调用、异步回调、多任务组合,是处理异步任务的核心类。

一、核心方法分类与用法

我把CompletableFuture的核心方法分为4大类,每类配可直接运行的示例代码,方便你理解。

1. 基础:创建异步任务

用于启动一个异步任务,核心是两个方法(区别:是否有返回值)。

方法 作用 示例
supplyAsync(Supplier<U>) 异步执行有返回值的任务 获取异步计算结果
runAsync(Runnable) 异步执行无返回值的任务 异步执行日志记录、通知等操作

示例代码

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

public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. supplyAsync:有返回值的异步任务(比如模拟查询商品价格)
        CompletableFuture<Integer> priceFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时操作(比如调用远程接口)
            try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
            return 99; // 商品价格
        });
        System.out.println("异步获取价格:" + priceFuture.get()); // 阻塞获取结果(仅演示,实际少用)

        // 2. runAsync:无返回值的异步任务(比如模拟发送通知)
        CompletableFuture<Void> notifyFuture = CompletableFuture.runAsync(() -> {
            try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("异步发送订单通知完成");
        });
        notifyFuture.get(); // 等待任务完成
    }
}

2. 核心:结果处理/转换

任务执行完成后,对结果进行转换、消费或异常处理(非阻塞回调)。

方法 作用 特点
thenApply(Function<T,U>) 转换结果(输入T,输出U) 有入参、有返回值
thenAccept(Consumer<T>) 消费结果(输入T) 有入参、无返回值
thenRun(Runnable) 任务完成后执行(无参数) 无入参、无返回值
whenComplete(BiConsumer<T,Throwable>) 处理结果+异常(不改变结果) 可捕获异常,返回原结果
exceptionally(Function<Throwable,T>) 异常兜底(返回默认值) 异常时触发,返回替代结果

示例代码(基于上面的priceFuture扩展):

java 复制代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> priceFuture = CompletableFuture.supplyAsync(() -> {
        // 模拟异常场景:随机抛出异常
        if (Math.random() > 0.5) {
            throw new RuntimeException("查询价格失败");
        }
        return 99;
    });

    // 1. thenApply:转换结果(价格→价格+运费)
    CompletableFuture<Integer> totalPriceFuture = priceFuture.thenApply(price -> price + 10);

    // 2. whenComplete:处理结果+异常(仅打印,不改变结果)
    totalPriceFuture.whenComplete((totalPrice, ex) -> {
        if (ex != null) {
            System.out.println("处理价格失败:" + ex.getMessage());
        } else {
            System.out.println("最终总价:" + totalPrice);
        }
    });

    // 3. exceptionally:异常兜底(失败时返回默认价格)
    CompletableFuture<Integer> finalFuture = totalPriceFuture.exceptionally(ex -> 0);
    System.out.println("兜底后的价格:" + finalFuture.get());
}

3. 进阶:多任务组合

处理多个异步任务的依赖或并行关系,是CompletableFuture最强大的特性。

方法 作用 适用场景
thenCompose(Function<T,CompletableFuture<U>>) 串联依赖任务(任务B依赖任务A的结果) 先查订单→再查订单详情
thenCombine(CompletableFuture<U>, BiFunction<T,U,V>) 合并两个独立任务的结果 并行查商品价格和库存,合并结果
allOf(CompletableFuture<?>...) 等待所有任务完成(无返回值) 并行处理多个无依赖的任务(如批量更新)
anyOf(CompletableFuture<?>...) 等待任一任务完成(返回第一个完成的结果) 多渠道查询数据,取最快的那个

示例代码

java 复制代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
    // 任务1:查询商品价格
    CompletableFuture<Integer> priceFuture = CompletableFuture.supplyAsync(() -> 99);
    // 任务2:查询商品库存(独立任务)
    CompletableFuture<Integer> stockFuture = CompletableFuture.supplyAsync(() -> 1000);

    // 1. thenCombine:合并两个独立任务的结果
    CompletableFuture<String> combineFuture = priceFuture.thenCombine(stockFuture, (price, stock) -> {
        return "商品价格:" + price + ",库存:" + stock;
    });
    System.out.println(combineFuture.get()); // 输出:商品价格:99,库存:1000

    // 2. thenCompose:串联依赖任务(先查价格→再算折扣价)
    CompletableFuture<Integer> discountFuture = priceFuture.thenCompose(price -> {
        // 第二个任务依赖第一个任务的结果
        return CompletableFuture.supplyAsync(() -> price * 9 / 10);
    });
    System.out.println("折扣价:" + discountFuture.get()); // 输出:折扣价:89

    // 3. allOf:等待所有任务完成(无返回值)
    CompletableFuture<Void> allFuture = CompletableFuture.allOf(priceFuture, stockFuture, discountFuture);
    allFuture.get(); // 等待所有任务完成
    System.out.println("所有任务执行完毕");

    // 4. anyOf:取第一个完成的任务结果
    CompletableFuture<String> fastFuture1 = CompletableFuture.supplyAsync(() -> {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        return "渠道1结果";
    });
    CompletableFuture<String> fastFuture2 = CompletableFuture.supplyAsync(() -> {
        try { Thread.sleep(500); } catch (InterruptedException e) {}
        return "渠道2结果";
    });
    CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(fastFuture1, fastFuture2);
    System.out.println("最快的结果:" + anyFuture.get()); // 输出:渠道2结果
}

4. 结果获取:get() vs join()

这两个方法都用于获取异步任务结果,核心区别是异常处理

  • get():抛出InterruptedExceptionExecutionException(检查异常),必须捕获或声明抛出;
  • join():抛出非检查异常(CompletionException),无需捕获,更适合流式调用。

示例

java 复制代码
// get()需要捕获异常
try {
    Integer price = priceFuture.get();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

// join()无需捕获异常(推荐在流式调用中使用)
Integer price = priceFuture.join();

二、关键注意事项

  1. 线程池supplyAsync/runAsync默认使用ForkJoinPool.commonPool(),如果任务耗时久/数量多,建议自定义线程池(避免占用公共线程池):

    java 复制代码
    // 自定义线程池
    ExecutorService executor = Executors.newFixedThreadPool(5);
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 99, executor);
  2. 异常处理 :务必通过whenComplete/exceptionally处理异常,否则异步任务的异常会被吞掉,难以排查;

  3. 避免阻塞 :尽量使用回调方法(thenApply/thenAccept等)替代get()/join()阻塞获取结果,发挥异步优势。

总结

  1. CompletableFuture核心分为4类方法:创建任务(supplyAsync/runAsync)、结果处理(thenApply/exceptionally)、任务组合(thenCompose/allOf)、结果获取(get/join)
  2. thenCompose用于串联依赖任务,thenCombine用于合并独立任务,allOf/anyOf用于多任务批量处理;
  3. 优先使用join()(非检查异常)和自定义线程池,同时做好异常兜底。
相关推荐
大傻^19 小时前
LangChain4j Spring Boot Starter:自动配置与声明式 Bean 管理
java·人工智能·spring boot·spring·langchain4j
沐硕19 小时前
《基于改进协同过滤与多目标优化的健康饮食推荐系统设计与实现》
java·python·算法·fastapi·多目标优化·饮食推荐·改进协同过滤
愣头不青20 小时前
560.和为k的子数组
java·数据结构
乱世军军20 小时前
把 Python 3.13 降级到 3.11
开发语言·python
本喵是FW20 小时前
C语言手记2
c语言·开发语言
fy1216320 小时前
GO 快速升级Go版本
开发语言·redis·golang
共享家952720 小时前
Java入门(String类)
java·开发语言
l软件定制开发工作室20 小时前
Spring开发系列教程(34)——打包Spring Boot应用
java·spring boot·后端·spring·springboot
0xDevNull20 小时前
Spring Boot 循环依赖解决方案完全指南
java·开发语言·spring
爱丽_20 小时前
GC 怎么判定“该回收谁”:GC Roots、可达性分析、四种引用与回收算法
java·jvm·算法