CompleteFuture 详解

1,032 阅读7分钟

Future 的局限性

Future 是 Java 5 引入的异步编程接口,虽然它解决了部分异步编程问题,但在实际使用中存在多个明显的局限性:

1. 阻塞获取结果

  • Future 只能通过 get() 方法阻塞获取结果,这会阻塞调用线程
  • 缺乏非阻塞回调机制,无法在任务完成后自动通知
Future<String> future = executor.submit(task);
String result = future.get(); // 阻塞直到结果可用

2. 无法组合多个 Future

  • 难以将多个 Future 的结果组合起来
  • 实现多个异步任务的依赖关系需要手动编码,代码复杂
Future<String> future1 = service.submit(task1);
Future<String> future2 = service.submit(task2);

String result1 = future1.get(); // 阻塞
String result2 = future2.get(); // 阻塞

String combined = result1 + result2;

3. 缺乏异常处理机制

  • 异常只能在调用 get() 时捕获,无法提前处理
  • 没有提供链式异常处理的方法

4. 无法手动完成

  • 不能手动设置 Future 的结果或异常
  • 无法实现超时自动完成等场景

5. 没有任务链式调用

  • 无法实现"任务A完成后自动触发任务B"的模式
  • 每个阶段都需要手动处理,代码冗长

6. 缺乏时间控制

  • 没有内置的超时控制(虽然 get() 可以带超时参数)
  • 无法实现"哪个任务先完成就先用哪个"的逻辑

7. 无法取消回调

  • 一旦提交任务,难以优雅地取消整个处理链

CompleteFuture

CompletableFuture 是 Java 8 引入的一个异步编程工具类,属于 java.util.concurrent 包,实现了 Future 和 CompletionStage 接口。它代表一个异步计算的结果,并提供了丰富的 API 来组合、转换和处理异步任务

核心特点:

  • 非阻塞:支持回调机制,避免 Future.get() 的阻塞问题
  • 链式调用:可以串联多个异步任务(类似 Stream 的流式操作)
  • 组合操作:支持多个 CompletableFuture 的组合(并行、串行、竞速等)
  • 异常处理:内置 exceptionally()handle() 等方法处理异常
  • 手动完成:可以手动设置任务结果(complete()completeExceptionally()

创建 CompleteFuture

  • supplyAsync() - 有返回值的异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟耗时操作
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
});

// 获取结果(会阻塞)
String result = future.get();
System.out.println(result);
  • runAsync() - 无返回值的异步任务
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // 模拟耗时操作
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    System.out.println("异步任务执行完成");
});

// 等待任务完成
future.get();

使用自定义线程池

ExecutorService customExecutor = Executors.newFixedThreadPool(5);

// 使用自定义线程池执行异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 任务逻辑
    return "使用自定义线程池执行的结果";
}, customExecutor); // 默认使用ForkJoinPool.commonPool()

异步任务结果处理

thenApply(Function<T, R>) — 同步转换结果

  • 作用:对上一个阶段的结果进行转换,返回一个新的值
  • 输入:上一个任务的返回值(T 类型)
  • 输出:返回转换后的结果(R 类型),可以继续传递给下一个 CompletableFuture
  • 适用场景:需要对结果进行映射或计算,例如字符串转大写、数字运算
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture<Integer> result = future.thenApply(s -> s.length()); // 计算字符串长度
System.out.println(result.get()); // 输出 5

thenAccept(Consumer<T>) — 消费结果

  • 作用:消费上一个阶段的结果,但不返回新值(返回 void
  • 输入:上一个任务的返回值(T 类型)
  • 输出:无返回值(CompletableFuture<Void>
  • 适用场景:仅需处理结果而不需要返回值的操作,如打印、存储
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture<Void> result = future.thenAccept(s -> System.out.println(s)); // 直接消费
result.join(); // 输出 "hello"

thenRun(Runnable) — 无结果执行

  • 作用:在上一个阶段完成后执行某个操作,不接收上一个任务的结果,也不返回新值
  • 输入:无(不关心前一个任务的结果)
  • 输出:无返回值(CompletableFuture<Void>
  • 适用场景:仅需在任务完成后执行某个操作,如日志记录、清理资源
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture<Void> result = future.thenRun(() -> System.out.println("Task finished"));
result.join(); // 输出 "Task finished"

以上方法均有对应的异步版本(带 Async 后缀,如 thenApplyAsync),表示回调会在新的线程中执行,默认使用 ForkJoinPool.commonPool()

组合多个 CompleteFuture

thenCompose() — 扁平化处理(链式异步调用)

  • 用于链式调用多个 CompletableFuture,避免嵌套(如 CompletableFuture<CompletableFuture<T>>
// 模拟查询订单(返回订单信息,包含 userId)
CompletableFuture<Order> getOrder(String orderId) {
    return CompletableFuture.supplyAsync(() -> {
        System.out.println("查询订单: " + orderId);
        return new Order(orderId, "user123"); // 假设订单包含 userId
    });
}

// 模拟查询用户(返回用户信息)
CompletableFuture<User> getUser(String userId) {
    return CompletableFuture.supplyAsync(() -> {
        System.out.println("查询用户: " + userId);
        return new User(userId, "张三", "zhangsan@example.com");
    });
}

// 使用 thenCompose 链式调用
CompletableFuture<User> userFuture = getOrder("order-123")
    .thenCompose(order -> getUser(order.getUserId())); // 避免嵌套 Future

User user = userFuture.join();
System.out.println("用户信息: " + user);
// 输出:
// 查询订单: order-123
// 查询用户: user123
// 用户信息: User{userId='user123', name='张三', email='zhangsan@example.com'}

thenCombine() — 合并两个独立任务的结果

- 合并 **两个独立的 `CompletableFuture`**,等它们都完成后,再对它们的结果进行组合计算

// 模拟查询推荐商品
CompletableFuture<List<String>> getRecommendations() {
    return CompletableFuture.supplyAsync(() -> {
        System.out.println("查询推荐商品...");
        return List.of("iPhone", "MacBook", "AirPods");
    });
}

// 模拟查询购物车商品
CompletableFuture<List<String>> getCartItems() {
    return CompletableFuture.supplyAsync(() -> {
        System.out.println("查询购物车商品...");
        return List.of("T-shirt", "Jeans");
    });
}

// 使用 thenCombine 合并两个 Future 的结果
CompletableFuture<List<String>> combinedFuture = getRecommendations()
    .thenCombine(
        getCartItems(),
        (recommendations, cartItems) -> {
            // 合并推荐商品和购物车商品
            List<String> allItems = new ArrayList<>(recommendations);
            allItems.addAll(cartItems);
            return allItems;
        }
    );

List<String> allItems = combinedFuture.join();
System.out.println("所有商品: " + allItems);
// 输出:
// 查询推荐商品...
// 查询购物车商品...
// 所有商品: [iPhone, MacBook, AirPods, T-shirt, Jeans]

allOf() — 等待所有 CompletableFuture 完成

  • 等待 所有给定的 CompletableFuture 完成(不关心它们的返回值)
  • 返回 CompletableFuture<Void>,仅表示所有任务已完成
public class OrderService {
    // 模拟发送短信(异步)
    CompletableFuture<Void> sendSMS(String orderId) {
        return CompletableFuture.runAsync(() -> {
            System.out.println("发送短信通知用户,订单号: " + orderId);
        });
    }

    // 模拟记录日志(异步)
    CompletableFuture<Void> logOrder(String orderId) {
        return CompletableFuture.runAsync(() -> {
            System.out.println("记录订单日志,订单号: " + orderId);
        });
    }

    // 模拟更新库存(异步)
    CompletableFuture<Void> updateStock(String orderId) {
        return CompletableFuture.runAsync(() -> {
            System.out.println("更新库存,订单号: " + orderId);
        });
    }

    // 使用 allOf 等待所有任务完成
    public CompletableFuture<Void> processOrder(String orderId) {
        CompletableFuture<Void> smsFuture = sendSMS(orderId);
        CompletableFuture<Void> logFuture = logOrder(orderId);
        CompletableFuture<Void> stockFuture = updateStock(orderId);

        return CompletableFuture.allOf(smsFuture, logFuture, stockFuture)
                .thenRun(() -> System.out.println("订单处理完成!"));
    }

    public static void main(String[] args) {
        OrderService service = new OrderService();
        service.processOrder("order-123").join();
    }
}

anyOf() — 等待任意一个 CompletableFuture 完成

  • 等待 任意一个给定的 CompletableFuture 完成(不管其他任务是否完成)
  • 返回 CompletableFuture<Object>(因为任务返回类型可能不同)
  • 多个任务并行执行,只要有一个完成就可以继续(如超时处理、竞速查询)
public class PriceService {
    // 模拟从缓存查询(快速,但可能数据过期)
    CompletableFuture<Double> getPriceFromCache(String productId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(100); // 模拟网络延迟
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("从缓存查询价格");
            return 99.9; // 假设缓存中的价格
        });
    }

    // 模拟从数据库查询(较慢,但数据准确)
    CompletableFuture<Double> getPriceFromDB(String productId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(500); // 模拟数据库查询延迟
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("从数据库查询价格");
            return 109.9; // 数据库中的价格
        });
    }

    // 模拟从第三方API查询(最慢,但可能更权威)
    CompletableFuture<Double> getPriceFromAPI(String productId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(800); // 模拟API延迟
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("从第三方API查询价格");
            return 119.9; // 第三方API的价格
        });
    }

    // 使用 anyOf 获取最快返回的价格
    public CompletableFuture<Double> getFastestPrice(String productId) {
        CompletableFuture<Double> cacheFuture = getPriceFromCache(productId);
        CompletableFuture<Double> dbFuture = getPriceFromDB(productId);
        CompletableFuture<Double> apiFuture = getPriceFromAPI(productId);

        return CompletableFuture.anyOf(cacheFuture, dbFuture, apiFuture)
                .thenApply(result -> (Double) result); // 类型转换
    }

    public static void main(String[] args) {
        PriceService service = new PriceService();
        Double price = service.getFastestPrice("product-123").join();
        System.out.println("最快返回的价格: " + price);
    }
}

CompleteFuture 高级特性

异常处理

exceptionally() 方法

exceptionally() 方法允许你提供一个回调函数,当 CompletableFuture 完成时出现异常时调用:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 可能抛出异常的操作
    if (Math.random() > 0.5) {
        throw new RuntimeException("Oops!");
    }
    return "Success";
}).exceptionally(ex -> {
    System.out.println("Exception occurred: " + ex.getMessage());
    return "Recovered Value";
});

System.out.println(safeFuture.join()); // 输出 "Success" 或 "Recovered Value"

handle() 方法

handle() 方法无论 CompletableFuture 是否正常完成都会被调用,可以同时处理正常结果和异常

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 可能抛出异常的操作
    if (Math.random() > 0.5) {
        throw new RuntimeException("Error!");
    }
    return "Success";
}).handle((result, ex) -> {
    if (ex != null) {
        return "Handled: " + ex.getMessage();
    }
    return "Result: " + result;
});

System.out.println(handledFuture.join());

whenComplete() 方法

whenComplete() 允许你在 CompletableFuture 完成时(无论是正常完成还是异常完成)执行指定的操作

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("模拟异常");
    }
    return "成功结果";
}).whenComplete((result, ex) -> {
    if (ex != null) {
        System.out.println("任务失败,异常: " + ex.getMessage());
    } else {
        System.out.println("任务成功,结果: " + result);
    }
});