CompletableFuture 全景深度解析与高性能实践手册:从源码到业务落地

前言:为什么你必须掌握 CompletableFuture?

如果你是 Java 开发者,大概率遇到过这些场景:

  • 调用多个接口组装数据时,串行执行太慢,想并行又怕线程池乱掉;
  • Future 拿结果时不得不阻塞等待,导致接口响应超时;
  • 异步任务嵌套太多,代码写成"回调金字塔",debug 时找不着北;
  • 线上突发 OutOfMemoryError,排查发现是线程池队列堆积了上万任务...

这些问题的根源,在于传统异步编程工具的局限性。而 CompletableFuture(简称 CF) 的出现,彻底改变了 Java 异步编程的游戏规则。作为 Java 8 引入的"异步编程瑞士军刀",它不仅解决了 Future 的痛点,更通过 链式编排、灵活组合、优雅异常处理 三大核心能力,让异步代码像同步代码一样易读、易维护。

本文将从 进化史→源码剖析→API 体系→实战场景→工具封装→技术延伸 六个维度,用 2 万字+的篇幅带你吃透 CF,不仅告诉你"怎么用",更让你明白"为什么这么设计",最终能在高并发业务中写出既优雅又高性能的异步代码。

一、异步编程进化论:从"步行"到"超音速"的三次跃迁

Java 异步编程的发展史,本质是"如何让程序更高效利用资源"的探索史。从最初的 Thread 到现在的 CompletableFuture,每一次升级都在解决前一代的痛点。

1.1 Java 5 之前:Thread + Runnable 的"步行时代"

在 Java 5 之前,异步编程只能靠 ThreadRunnable 硬写:

// 原始异步写法:手动创建线程
new Thread(new Runnable() {
    @Override
    public void run() {
        // 异步任务:查数据库
        User user = userDao.queryById(1L);
    }
}).start();

这种方式的问题堪称"灾难性":

  • 资源浪费:每启动一个任务就创建一个线程,线程创建销毁成本高(单个线程栈内存默认 1MB);
  • 无法管理:线程分散在代码各处,没有统一调度,高并发下容易"线程爆炸";
  • 结果难拿:想获取异步任务结果只能用共享变量,还得自己加锁,一不小心就死锁;
  • 无法组合:多个异步任务想按顺序执行?只能嵌套 Thread,代码直接变成"千层饼"。

1.2 Java 5:Future + ExecutorService 的"自行车时代"

Java 5 引入 Future 接口和线程池 ExecutorService,首次实现了"任务提交"与"结果获取"的解耦:

// 线程池 + Future 写法
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<User> userFuture = executor.submit(new Callable<User>() {
    @Override
    public User call() throws Exception {
        return userDao.queryById(1L);
    }
});

// 主线程干其他事...
doSomethingElse();

// 拿结果(阻塞等待)
User user = userFuture.get(); // 阻塞!如果任务没完成,主线程卡在这里

这代改进解决了线程管理问题,但新的痛点又出现了:

  • 阻塞获取get() 方法会阻塞当前线程,想非阻塞拿结果只能用 isDone() 轮询(低效且浪费 CPU);
  • 无法链式组合:想实现"查用户→查订单→查物流"的流程,只能嵌套 Future.get(),回调地狱重现;
  • 异常处理拉垮:任务抛出的异常会被包装成 ExecutionException,想拿到原始异常得层层剥壳,链路断裂;
  • 功能单一:没有批量任务处理、超时控制等高级功能,复杂场景下还得自己封装。

1.3 Java 8:CompletableFuture 的"电动车时代"

Java 8 推出的 CompletableFuture 彻底解决了这些问题。它同时实现 FutureCompletionStage 接口,把"异步任务的创建、组合、回调、异常、取消"全部纳入统一的 DSL 体系:

// CompletableFuture 链式写法
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> {
    return userDao.queryById(1L); // 异步查用户
}, executor)
.thenApply(user -> { // 用户查完后查订单
    return orderDao.queryByUserId(user.getId());
})
.thenApply(order -> { // 订单查完后查物流
    return logisticsDao.queryByOrderId(order.getId());
})
.exceptionally(ex -> { // 全局异常处理
    log.error("异步任务失败", ex);
    return null;
});

CF 的核心进化点在于:

  • 非阻塞回调:用 thenApply 等方法注册回调,任务完成后自动执行,无需阻塞等待;
  • 链式组合:多个异步任务可以像链表一样串起来,流程清晰,告别嵌套;
  • 丰富的组合方式:支持"全完成"(allOf)、"任一完成"(anyOf)等批量操作;
  • 完善的异常处理exceptionallyhandle 等方法覆盖各种异常场景,链路完整;
  • 线程池灵活控制:支持自定义线程池,避免默认线程池被"卡死"。

1.4 Java 9+:Flow API 的"高铁时代"

Java 9 基于 CF 进一步抽象出 Flow API,进入反应式编程领域。Flow 定义了 发布者(Publisher)、订阅者(Subscriber)、订阅(Subscription)、处理器(Processor) 四大接口,支持 背压(Backpressure) 机制(订阅者可以告诉发布者"慢点发,我处理不过来")。

Flow 更适合处理 持续数据流场景(如实时日志处理、消息推送),而日常业务中的"异步任务编排"(如接口组装、批量处理),CF 仍是最优选择——它更轻量、学习成本更低,且能满足 90% 的业务需求。

1.5 为什么 CompletableFuture 是最佳选择?

对比三代异步工具,CF 的优势一目了然:

特性Thread + RunnableFuture + ExecutorServiceCompletableFuture
线程管理手动创建,资源浪费线程池统一管理支持自定义线程池,更灵活
结果获取需共享变量+锁阻塞 get() 或轮询非阻塞回调,自动触发
任务组合嵌套 Thread,可读性差嵌套 get(),回调地狱链式调用,支持 allOf/anyOf
异常处理try-catch 仅限单任务异常被包装,链路断裂exceptionally/handle 全链路处理
高级功能基本无超时控制、手动完成、并行加速等

结论:在 Java 异步编程中,CompletableFuture 是"性价比之王"——既解决了传统工具的痛点,又不像反应式框架(如 RxJava)那样有陡峭的学习曲线。

二、源码级剖析:CompletableFuture 为什么这么能打?

要真正用好 CF,必须理解它的底层设计。CF 的高性能和灵活性,源于其 精巧的数据结构无锁并发设计

2.1 核心数据结构:结果 + 回调栈

CF 内部靠两个核心字段支撑所有功能:

(1)volatile Object result:存储任务结果或异常

  • 正常完成时,result 直接存任务返回值(如 StringUser 等);
  • 异常完成时,resultAltResult 对象(包装了异常信息和取消状态);
  • volatile 保证多线程可见性,一个线程设置结果后,其他线程能立即看到。

(2)volatile Completion stack:存储后续回调任务

stack 是一个 Treiber 栈(无锁并发栈),用于存储所有注册的回调任务(如 thenApplythenCombine 等方法传入的逻辑)。Treiber 栈的特点是:

  • 用 CAS 操作实现无锁入栈/出栈,并发安全且性能高;
  • 回调任务按注册顺序入栈,完成时按"后进先出"顺序执行(但最终通过 postComplete() 保证顺序正确)。

Completion 是所有回调任务的基类,不同的回调方法对应不同的子类:

  • UniApply:对应 thenApply 方法,处理单个前置任务的结果;
  • BiRelay:对应 thenCombine 方法,处理两个前置任务的结果;
  • AndJoin:对应 allOf 方法,等待所有前置任务完成。

2.2 状态机设计:无锁 CAS 保证并发安全

CF 有一套完整的状态机,所有状态转换都通过 CAS 操作完成,全程无锁,性能极高。状态定义如下:

状态常量含义说明触发条件
UNCLAIMED初始状态,任务未开始或正在执行刚创建 CF 时的默认状态
COMPLETING临时状态,正在设置结果任务执行完,准备设置 result
NORMAL正常完成状态任务成功返回结果
EXCEPTIONAL异常完成状态任务执行中抛出异常
CANCELLED取消状态调用 cancel() 方法

状态流转路径:

  • 正常流程:UNCLAIMED → COMPLETING → NORMAL
  • 异常流程:UNCLAIMED → COMPLETING → EXCEPTIONAL
  • 取消流程:UNCLAIMED → COMPLETING → CANCELLED

2.3 关键方法源码解析

(1)supplyAsync:创建带返回值的异步任务

supplyAsync 是创建 CF 最常用的方法,用于提交有返回值的异步任务。源码如下:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(asyncPool, supplier); // asyncPool 是默认线程池(ForkJoinPool.commonPool())
}

static <U> CompletableFuture<U> asyncSupplyStage(Executor e, Supplier<U> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> d = new CompletableFuture<U>(); // 创建新的 CF 实例
    e.execute(new AsyncSupply<U>(d, f)); // 提交任务到线程池
    return d;
}

AsyncSupply 是实现 Runnable 的内部类,其 run() 方法是核心:

static final class AsyncSupply<U> implements Runnable {
    final CompletableFuture<U> dep; // 关联的 CF 实例
    final Supplier<U> fn; // 用户传入的任务逻辑

    AsyncSupply(CompletableFuture<U> dep, Supplier<U> fn) {
        this.dep = dep;
        this.fn = fn;
    }

    public void run() {
        U result;
        try {
            result = fn.get(); // 执行用户任务
        } catch (Throwable ex) {
            dep.completeExceptionally(ex); // 异常时设置异常结果
            return;
        }
        dep.complete(result); // 正常时设置结果
    }
}

流程解析:

  1. 调用 supplyAsync 时,创建一个新的 CompletableFuture 实例 d
  2. 把用户任务 Supplier 包装成 AsyncSupply(实现 Runnable),提交到线程池;
  3. 线程池执行 AsyncSupply.run()
    • 正常执行:调用 fn.get() 拿到结果,通过 dep.complete(result) 设置 d.result,状态切为 NORMAL
    • 异常执行:捕获异常,通过 dep.completeExceptionally(ex) 设置异常结果,状态切为 EXCEPTIONAL
  4. 结果设置后,触发 postComplete() 方法,执行 stack 中的所有回调任务。

(2)thenApply:链式处理任务结果

thenApply 用于对前置任务的结果进行转换,是链式调用的核心方法。源码如下:

public <T> CompletableFuture<T> thenApply(Function<? super U,? extends T> fn) {
    return uniApplyStage(null, fn); // null 表示用当前线程同步执行回调
}

private <V> CompletableFuture<V> uniApplyStage(Executor e, Function<? super T,? extends V> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<V> d = new CompletableFuture<V>(); // 新的 CF 实例,用于承载转换结果
    if (!d.uniApply(this, f, e)) { // 快速路径:如果前置任务已完成,直接执行转换
        // 前置任务未完成,把回调压入前置任务的 stack 中等待
        push(new UniApply<T,V>(d, this, f, e));
    }
    return d;
}

UniApplyCompletion 的子类,负责实际执行转换逻辑:

static final class UniApply<T,V> extends UniCompletion<T,V> {
    Function<? super T,? extends V> fn;

    UniApply(CompletableFuture<V> dep, CompletableFuture<T> src,
             Function<? super T,? extends V> fn, Executor e) {
        super(dep, src, e);
        this.fn = fn;
    }

    final void tryFire(int mode) {
        CompletableFuture<V> d = dep;
        CompletableFuture<T> s = src;
        if (d == null || s == null || !d.uniApply(s, fn, executor))
            return;
        dep = null; src = null; fn = null; // 释放资源
    }
}

流程解析:

  1. 调用 thenApply 时,创建新的 CompletableFuture 实例 d(用于存储转换结果);
  2. 检查前置任务是否已完成:
    • 已完成:直接调用 uniApply 执行 Function 转换,结果存入 d
    • 未完成:创建 UniApply 实例,通过 push 方法压入前置任务的 stack 中;
  3. 前置任务完成后,postComplete() 会弹出 stack 中的 UniApply,调用 tryFire() 执行转换,最终 d 完成。

(3)allOf:等待所有任务完成

allOf 用于批量处理多个 CF 任务,等待所有任务完成后再执行后续操作。源码简化如下:

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    return whenAllComplete(cfs).thenApply(v -> null);
}

private static CompletableFuture<Object> whenAllComplete(CompletableFuture<?>... cfs) {
    if (cfs == null || cfs.length == 0)
        return completedFuture(null);
    // 创建一个计数工具,等待所有任务完成
    CountedCompleter<Object> cc = new WhenAllComplete(cfs, null, cfs.length);
    // 启动计数等待
    cc.invoke();
    return cc;
}

WhenAllComplete 继承 CountedCompleter(Fork/Join 框架中的计数完成器):

  • 初始化时计数为任务数量 n
  • 每个任务完成后,计数减 1;
  • 当计数减到 0 时,触发完成逻辑,allOf 返回的 CF 进入完成状态。

注意allOf 返回的是 CompletableFuture<Void>,无法直接获取所有任务的结果,需要手动调用每个任务的 get()join() 方法获取。

2.4 性能核心:无锁 + 批处理回调

CF 的高性能源于两点设计:

(1)无锁并发,CAS 操作替代 synchronized

CF 中所有状态转换(设置结果、入栈/出栈回调)都用 CAS 操作,避免了 synchronized 的性能损耗。例如 complete 方法的核心逻辑:

public boolean complete(U value) {
    if (result == null && UNSAFE.compareAndSwapObject(this, RESULT, null,
                                                       (value == null) ? NIL : value)) {
        postComplete(); // 设置成功后触发回调
        return true;
    }
    return false;
}

通过 UNSAFE.compareAndSwapObject 原子设置结果,并发场景下性能远高于加锁。

(2)postComplete() 批处理回调,减少上下文切换

当任务完成后,postComplete() 会一次性处理所有回调,而不是每次注册回调都立即执行:

private void postComplete() {
    Completion h = stack;
    if (h != null) {
        stack = null; // 清空栈
        for (;;) { // 循环处理所有回调
            CompletableFuture<?> d;
            Completion next;
            if ((d = h.dep) == null || !h.tryFire(NESTED)) {
                next = h.next;
                if (next == null)
                    break;
                h.next = null; // 帮助 GC
                h = next;
            } else {
                next = h.next;
                h.next = null; // 帮助 GC
                h = next;
            }
        }
    }
}

批处理回调减少了线程切换次数,尤其在回调链较长时,性能优势明显。官方基准测试显示:在 8 线程场景下,CF 的吞吐量比 FutureTask 高 30% 以上。

三、核心 API 体系化梳理:从基础到高级全掌握

CF 的 API 看似繁杂,实则有清晰的分类逻辑。按"任务依赖关系"可分为三大类:一元操作(单个前置任务)、二元操作(两个前置任务)、多元操作(多个前置任务)。

3.1 一元操作:单个任务的后续处理

一元操作是指"基于单个前置任务结果"的回调,核心方法如下:

方法作用返回值类型线程执行方式
thenApply转换前置任务结果CompletableFuture<R>同步(当前线程)或异步(指定线程池)
thenAccept消费前置任务结果(无返回值)CompletableFuture<Void>同上
thenRun前置任务完成后执行动作CompletableFuture<Void>同上
thenCompose扁平化处理(避免嵌套 CF)CompletableFuture<R>同上
whenComplete完成后处理(结果+异常)CompletableFuture<U>同步(当前线程)
exceptionally异常时返回兜底结果CompletableFuture<U>同步(当前线程)
handle统一处理正常/异常结果CompletableFuture<R>同步(当前线程)

(1)thenApply vs thenCompose:转换结果的两种方式

  • thenApply:用于"同步转换",输入是前置结果,输出是新结果,返回 CompletableFuture<R>

    // 示例:查询用户后转换为用户名
    CompletableFuture<String> usernameFuture = CompletableFuture.supplyAsync(() -> {
        return userDao.queryById(1L); // 返回 User
    }).thenApply(user -> user.getUsername()); // 转换为 String
    
    
  • thenCompose:用于"异步转换",输入是前置结果,输出是另一个 CompletableFuture,避免嵌套 CF(扁平化);

    // 示例:查询用户后异步查订单(避免嵌套)
    CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(() -> {
        return userDao.queryById(1L); // 返回 User
    }).thenCompose(user -> { // 输入 User,输出 CompletableFuture<Order>
        return CompletableFuture.supplyAsync(() -> orderDao.queryByUserId(user.getId()));
    });
    
    

区别thenApply 会把结果包装成 CompletableFuture<CompletableFuture<R>>(嵌套),而 thenCompose 直接返回 CompletableFuture<R>(扁平)。

(2)whenComplete vs handle vs exceptionally:异常处理三兄弟

  • whenComplete:无论正常/异常都执行,接收结果和异常,无返回值(常用于日志记录);

    CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> {
        if (Math.random() > 0.5) throw new RuntimeException("查询失败");
        return userDao.queryById(1L);
    }).whenComplete((user, ex) -> { // 结果和异常都能拿到
        if (ex != null) {
            log.error("查询用户异常", ex);
        } else {
            log.info("查询用户成功: {}", user);
        }
    });
    
    
  • handle:类似 whenComplete,但有返回值,可转换结果或处理异常;

    CompletableFuture<String> resultFuture = CompletableFuture.supplyAsync(() -> {
        if (Math.random() > 0.5) throw new RuntimeException("查询失败");
        return userDao.queryById(1L);
    }).handle((user, ex) -> { // 处理并返回新结果
        if (ex != null) {
            return "默认用户名";
        } else {
            return user.getUsername();
        }
    });
    
    
  • exceptionally:仅异常时执行,返回兜底结果(相当于 catch 块);

    CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> {
        if (Math.random() > 0.5) throw new RuntimeException("查询失败");
        return userDao.queryById(1L);
    }).exceptionally(ex -> { // 异常时返回默认用户
        log.error("查询异常", ex);
        return new User(0L, "默认用户");
    });
    
    

3.2 二元操作:两个任务的协同处理

二元操作用于"两个前置任务都完成"或"任一完成"后的处理,核心方法如下:

方法作用适用场景
thenCombine两个任务都完成后,合并结果需要两个结果共同处理(AND)
thenAcceptBoth两个任务都完成后,消费结果同上,但无返回值
runAfterBoth两个任务都完成后,执行动作不关心结果,只关心完成事件
applyToEither任一任务完成后,转换其结果取最快完成的结果(OR)
acceptEither任一任务完成后,消费其结果同上,但无返回值
runAfterEither任一任务完成后,执行动作不关心结果,只关心谁先完成

(1)thenCombine:合并两个任务的结果

场景:电商下单时,需同时查库存和用户积分,合并结果判断是否可下单。

// 查库存
CompletableFuture<Integer> stockFuture = CompletableFuture.supplyAsync(() -> {
    return stockService.getStock(1001L); // 商品ID=1001的库存
});

// 查用户积分
CompletableFuture<Integer> pointFuture = CompletableFuture.supplyAsync(() -> {
    return userService.getPoints(1L); // 用户ID=1的积分
});

// 合并结果
CompletableFuture<Boolean> canOrderFuture = stockFuture.thenCombine(pointFuture,
    (stock, points) -> stock > 0 && points >= 100 // 库存>0且积分≥100可下单
);

(2)applyToEither:取两个任务中最快的结果

场景:多数据源查询,哪个快用哪个(比如查缓存和数据库,取最快的结果)。

// 查缓存(快)
CompletableFuture<User> cacheFuture = CompletableFuture.supplyAsync(() -> {
    return cacheService.get("user:1");
});

// 查数据库(慢)
CompletableFuture<User> dbFuture = CompletableFuture.supplyAsync(() -> {
    return userDao.queryById(1L);
});

// 取最快的结果
CompletableFuture<User> fastFuture = cacheFuture.applyToEither(dbFuture,
    user -> user // 哪个先完成就用哪个结果
);

3.3 多元操作:批量任务的集中处理

多元操作用于处理"多个前置任务"的场景,核心方法是 allOfanyOf

(1)allOf:等待所有任务完成

场景:批量查询多个商品信息,全部查完后汇总。

// 创建多个查询任务
List<Long> productIds = Arrays.asList(1001L, 1002L, 1003L);
List<CompletableFuture<Product>> futures = productIds.stream()
    .map(id -> CompletableFuture.supplyAsync(() -> productDao.queryById(id)))
    .collect(Collectors.toList());

// 等待所有任务完成
CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0])
);

// 汇总结果
CompletableFuture<List<Product>> resultFuture = allDoneFuture.thenApply(v ->
    futures.stream()
        .map(CompletableFuture::join) // 此时任务已完成,join() 不会阻塞
        .collect(Collectors.toList())
);

(2)anyOf:等待任一任务完成

场景:调用多个支付渠道,哪个成功用哪个(支付场景常用)。

// 支付宝支付
CompletableFuture<PaymentResult> aliPayFuture = CompletableFuture.supplyAsync(() -> {
    return aliPayService.pay(orderId, amount);
});

// 微信支付
CompletableFuture<PaymentResult> wxPayFuture = CompletableFuture.supplyAsync(() -> {
    return wxPayService.pay(orderId, amount);
});

// 任一支付成功即返回
CompletableFuture<Object> firstSuccessFuture = CompletableFuture.anyOf(
    aliPayFuture, wxPayFuture
);

// 处理结果(注意返回的是 Object,需要强转)
CompletableFuture<PaymentResult> paymentResultFuture = firstSuccessFuture.thenApply(result ->
    (PaymentResult) result
);

3.4 带 Async 后缀的方法:控制线程执行策略

CF 中很多方法有 Async 后缀版本(如 thenApplyAsyncthenCombineAsync),它们的区别在于 执行回调的线程

  • 不带 Async:回调在前置任务的线程中同步执行(可能是提交任务的线程,也可能是执行任务的线程);
  • Async(无线程池参数):回调在默认线程池(ForkJoinPool.commonPool())中异步执行;
  • Async(有线程池参数):回调在指定线程池中异步执行。

示例

CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> {
    // 任务1:在默认线程池执行
    return userDao.queryById(1L);
}, IO_POOL) // 自定义IO线程池
.thenApplyAsync(user -> {
    // 回调1:在指定线程池(CPU_POOL)执行
    return user.getUsername();
}, CPU_POOL)
.thenAcceptAsync(username -> {
    // 回调2:在默认线程池执行
    log.info("用户名: {}", username);
});

最佳实践

  • IO 密集型回调(如查库、调接口)用 IO_POOL(线程数多);
  • CPU 密集型回调(如数据计算、序列化)用 CPU_POOL(线程数=CPU核心);
  • 避免滥用默认线程池,防止被其他任务阻塞。

四、高性能实战:从业务场景到代码落地

理论懂了还不够,真正的高手能在复杂业务中用 CF 写出既优雅又高性能的代码。本节结合电商、支付、数据聚合等高频场景,带你掌握实战技巧。

4.1 电商订单处理:多任务并行加速

场景:用户下单流程需执行 4 步:验证订单→锁定库存→扣减积分→生成订单。其中"验证订单"和"锁定库存"可并行,"扣减积分"依赖"验证订单","生成订单"依赖前三者都完成。

传统串行流程:总耗时 = 验证(100ms) + 锁库存(150ms) + 扣积分(100ms) + 生成订单(200ms) = 550ms。

CF 并行流程:总耗时 = max(验证, 锁库存) + 扣积分 + 生成订单 = max(100,150) + 100 + 200 = 450ms,性能提升 18%。

代码实现:

// 1. 定义线程池(IO密集型,线程数=2*CPU核心)
private static final ExecutorService ORDER_EXECUTOR = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors() * 2,
    20, // 最大线程数
    60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000), // 任务队列
    new ThreadFactoryBuilder().setNameFormat("order-pool-%d").build(), // 线程命名
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用者执行,避免任务丢失
);

// 2. 订单处理主流程
public CompletableFuture<OrderResult> processOrder(OrderRequest request) {
    // 步骤1:异步验证订单(用户合法性、商品有效性)
    CompletableFuture<Boolean> validateFuture = CompletableFuture.supplyAsync(() -> {
        log.info("验证订单: {}", request.getOrderId());
        return orderValidator.validate(request); // 耗时~100ms
    }, ORDER_EXECUTOR);

    // 步骤2:异步锁定库存(与验证并行)
    CompletableFuture<StockLockResult> stockLockFuture = CompletableFuture.supplyAsync(() -> {
        log.info("锁定库存: 商品={}, 数量={}", request.getProductId(), request.getQuantity());
        return stockService.lockStock(request.getProductId(), request.getQuantity()); // 耗时~150ms
    }, ORDER_EXECUTOR);

    // 步骤3:验证通过后扣减积分(依赖验证结果)
    CompletableFuture<PointResult> pointFuture = validateFuture.thenComposeAsync(valid -> {
        if (!valid) {
            throw new IllegalArgumentException("订单验证失败");
        }
        log.info("扣减积分: 用户={}, 积分={}", request.getUserId(), request.getPoints());
        return CompletableFuture.supplyAsync(() ->
            pointService.deductPoints(request.getUserId(), request.getPoints()), // 耗时~100ms
            ORDER_EXECUTOR
        );
    }, ORDER_EXECUTOR);

    // 步骤4:等待锁库存和扣积分完成后,生成订单
    return CompletableFuture.allOf(stockLockFuture, pointFuture)
        .thenApplyAsync(v -> {
            try {
                StockLockResult stockResult = stockLockFuture.get();
                PointResult pointResult = pointFuture.get();
                log.info("生成订单: 订单ID={}", request.getOrderId());
                return orderService.createOrder(request, stockResult, pointResult); // 耗时~200ms
            } catch (Exception e) {
                throw new CompletionException("生成订单失败", e);
            }
        }, ORDER_EXECUTOR)
        // 全局异常处理
        .exceptionally(ex -> {
            Throwable rootEx = CFExceptionUtil.rootCause(ex); // 工具类:获取根异常
            log.error("订单处理失败: {}", rootEx.getMessage(), rootEx);
            return new OrderResult(request.getOrderId(), "FAILED", rootEx.getMessage());
        });
}

优化点解析:

  1. 并行执行无关任务:验证订单和锁定库存并行,减少总耗时;
  2. 线程池隔离:用专用 ORDER_EXECUTOR,避免与其他业务线程池冲突;
  3. 异常全链路捕获exceptionally 统一处理所有步骤的异常,避免遗漏;
  4. 非阻塞获取结果allOf 后用 get() 获取结果(此时任务已完成,无阻塞)。

4.2 数据聚合场景:批量接口并行查询

场景:商品详情页需要聚合 5 类数据:基本信息、价格、库存、评价、推荐商品。传统串行调用 5 个接口耗时 800ms,用 CF 并行调用可压缩到 200ms(取决于最慢的接口)。

代码实现:

// 1. 定义IO线程池(接口调用是IO密集型)
private static final ExecutorService API_EXECUTOR = new ThreadPoolExecutor(
    16, 32, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(10000),
    new ThreadFactoryBuilder().setNameFormat("api-pool-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

// 2. 聚合商品详情数据
public CompletableFuture<ProductDetail> getProductDetail(Long productId) {
    // 并行查询5类数据
    CompletableFuture<ProductBase> baseFuture = CompletableFuture.supplyAsync(() ->
        productApi.getBaseInfo(productId), API_EXECUTOR
    );

    CompletableFuture<ProductPrice> priceFuture = CompletableFuture.supplyAsync(() ->
        priceApi.getPrice(productId), API_EXECUTOR
    );

    CompletableFuture<Integer> stockFuture = CompletableFuture.supplyAsync(() ->
        stockApi.getStock(productId), API_EXECUTOR
    );

    CompletableFuture<List<Comment>> commentFuture = CompletableFuture.supplyAsync(() ->
        commentApi.getComments(productId, 1, 10), API_EXECUTOR
    );

    CompletableFuture<List<Product>> recommendFuture = CompletableFuture.supplyAsync(() ->
        recommendApi.getRecommendProducts(productId), API_EXECUTOR
    );

    // 等待所有数据查询完成,聚合结果
    return CompletableFuture.allOf(
        baseFuture, priceFuture, stockFuture, commentFuture, recommendFuture
    ).thenApplyAsync(v -> {
        // 聚合所有结果
        ProductDetail detail = new ProductDetail();
        detail.setBase(baseFuture.join());
        detail.setPrice(priceFuture.join());
        detail.setStock(stockFuture.join());
        detail.setComments(commentFuture.join());
        detail.setRecommendProducts(recommendFuture.join());
        return detail;
    }, API_EXECUTOR)
    // 超时控制:最多等2秒
    .completeOnTimeout(new ProductDetail(), 2000, TimeUnit.MILLISECONDS)
    // 异常处理:返回降级结果
    .exceptionally(ex -> {
        log.error("聚合商品详情失败", ex);
        return new ProductDetail(); // 降级返回空详情
    });
}

优化点解析:

  1. 并行调用接口:5 个接口并行执行,总耗时=最慢接口耗时(假设 200ms);
  2. 超时保护completeOnTimeout 避免单个接口超时导致整体阻塞;
  3. 降级策略:异常时返回默认详情,保证页面不崩溃;
  4. 专用线程池API_EXECUTOR 线程数多(16 核 CPU 配 16 线程),适合 IO 密集型接口调用。

4.3 支付场景:多渠道并行尝试 + 超时控制

场景:用户支付时,同时调用支付宝、微信、银联三个渠道,任一成功即返回,超时未成功则提示失败。

代码实现:

// 1. 支付渠道线程池(IO密集型)
private static final ExecutorService PAY_EXECUTOR = new ThreadPoolExecutor(
    8, 16, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000),
    new ThreadFactoryBuilder().setNameFormat("pay-pool-%d").build(),
    new ThreadPoolExecutor.AbortPolicy() // 支付任务不允许丢失,失败需重试
);

// 2. 定时任务线程池(用于超时控制)
private static final ScheduledExecutorService TIMEOUT_EXECUTOR = Executors.newScheduledThreadPool(
    2, new ThreadFactoryBuilder().setNameFormat("timeout-pool-%d").build()
);

// 3. 多渠道支付
public CompletableFuture<PaymentResult> pay(Order order) {
    // 支付宝支付
    CompletableFuture<PaymentResult> aliFuture = createPayTask(
        () -> aliPayClient.pay(order.getOrderId(), order.getAmount())
    );

    // 微信支付
    CompletableFuture<PaymentResult> wxFuture = createPayTask(
        () -> wxPayClient.pay(order.getOrderId(), order.getAmount())
    );

    // 银联支付
    CompletableFuture<PaymentResult> unionFuture = createPayTask(
        () -> unionPayClient.pay(order.getOrderId(), order.getAmount())
    );

    // 任一渠道成功即返回,30秒超时
    return CompletableFuture.anyOf(aliFuture, wxFuture, unionFuture)
        .thenApply(result -> (PaymentResult) result)
        .completeOnTimeout(
            new PaymentResult(order.getOrderId(), "TIMEOUT", "支付超时"),
            30, TimeUnit.SECONDS
        )
        .exceptionally(ex -> {
            log.error("支付失败", ex);
            return new PaymentResult(order.getOrderId(), "FAILED", "支付渠道异常");
        });
}

// 创建带超时和异常处理的支付任务
private CompletableFuture<PaymentResult> createPayTask(Supplier<PaymentResult> paySupplier) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            return paySupplier.get();
        } catch (Exception e) {
            log.error("支付渠道调用失败", e);
            throw new CompletionException(e); // 包装异常,让 anyOf 忽略失败任务
        }
    }, PAY_EXECUTOR);
}

优化点解析:

  1. 多渠道并行:同时调用三个支付渠道,哪个快用哪个,减少用户等待;
  2. 超时兜底:30 秒未支付成功则返回超时,避免无限等待;
  3. 异常隔离:单个渠道失败不影响其他渠道,anyOf 会等待成功的渠道;
  4. 专用线程池:支付任务用 PAY_EXECUTOR,避免与其他任务冲突。

4.4 批量数据处理:分片并行加速

场景:需要处理 10 万条用户数据(如批量更新用户标签),单线程处理耗时 10 分钟,用 CF 分片并行处理可压缩到 1 分钟。

代码实现:

// 1. CPU密集型线程池(数据处理是CPU密集型)
private static final ExecutorService BATCH_EXECUTOR = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors(), // 线程数=CPU核心数
    Runtime.getRuntime().availableProcessors(),
    60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000), // 有界队列,避免OOM
    new ThreadFactoryBuilder().setNameFormat("batch-pool-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

// 2. 分片并行处理用户数据
public CompletableFuture<Void> batchUpdateUserTags(List<Long> userIds) {
    int batchSize = 1000; // 每批处理1000个用户
    List<List<Long>> batches = Lists.partition(userIds, batchSize); // 分片

    // 每批创建一个异步任务
    List<CompletableFuture<Void>> batchFutures = batches.stream()
        .map(batch -> CompletableFuture.runAsync(() -> {
            // 处理单批用户数据(CPU密集型:计算标签、更新数据库)
            for (Long userId : batch) {
                List<String> tags = userTagService.calculateTags(userId); // 计算标签
                userService.updateTags(userId, tags); // 更新标签
            }
        }, BATCH_EXECUTOR))
        .collect(Collectors.toList());

    // 等待所有批次完成
    return CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0]))
        .exceptionally(ex -> {
            log.error("批量更新用户标签失败", ex);
            throw new CompletionException(ex); // 继续抛出异常,让调用方感知
        });
}

优化点解析:

  1. 分片处理:大任务拆成小批次,避免单个任务耗时过长;
  2. CPU 线程池优化:线程数=CPU核心数,避免线程切换损耗;
  3. 有界队列:用 ArrayBlockingQueue 限制队列大小,防止任务堆积导致 OOM;
  4. 异常传递exceptionally 重新抛出异常,确保调用方知道处理结果。

五、通用工具类封装:让 CompletableFuture 用起来更顺手

重复造轮子是低效的,将 CF 的常用操作封装成工具类,能显著提升开发效率。参考开源项目 collection-complete 的设计,我们可以封装以下工具类。

5.1 CompletableFutureUtils:核心工具类

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
public final class CompletableFutureUtils {

    // 默认IO线程池(可根据业务调整)
    private static final ExecutorService DEFAULT_IO_POOL = new ThreadPoolExecutor(
        16, 32, 60, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(10000),
        new ThreadFactory() {
            private final AtomicInteger counter = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "default-io-pool-" + counter.getAndIncrement());
            }
        },
        new ThreadPoolExecutor.CallerRunsPolicy()
    );

    /**
     * 将 List<CompletableFuture<T>> 转换为 CompletableFuture<List<T>>
     * 用于批量任务结果聚合
     */
    public static <T> CompletableFuture<List<T>> sequence(Collection<CompletableFuture<T>> futures) {
        if (futures.isEmpty()) {
            return CompletableFuture.completedFuture(Collections.emptyList());
        }

        // 等待所有任务完成
        CompletableFuture<Void> allDone = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0])
        );

        // 聚合结果(过滤null值)
        return allDone.thenApply(v -> futures.stream()
            .map(future -> {
                try {
                    return future.join(); // join() 不抛受检异常,更方便
                } catch (CompletionException e) {
                    log.error("任务执行失败", e.getCause());
                    return null; // 失败任务返回null,后续过滤
                }
            })
            .filter(Objects::nonNull) // 过滤失败的任务结果
            .collect(Collectors.toList())
        );
    }

    /**
     * 并行处理集合元素,带超时和异常降级
     * @param source 源集合
     * @param mapper 处理函数(将T转换为R)
     * @param timeoutMs 超时时间(毫秒)
     * @param fallback 异常或超时的兜底值
     * @return 处理后的结果集合
     */
    public static <T, R> CompletableFuture<List<R>> parallelProcess(
            Collection<T> source,
            Function<T, R> mapper,
            long timeoutMs,
            R fallback) {
        return parallelProcess(source, mapper, DEFAULT_IO_POOL, timeoutMs, fallback);
    }

    /**
     * 并行处理集合元素(支持自定义线程池)
     */
    public static <T, R> CompletableFuture<List<R>> parallelProcess(
            Collection<T> source,
            Function<T, R> mapper,
            Executor executor,
            long timeoutMs,
            R fallback) {
        if (source.isEmpty()) {
            return CompletableFuture.completedFuture(Collections.emptyList());
        }

        // 每个元素创建一个异步任务
        List<CompletableFuture<R>> futures = source.stream()
            .map(t -> CompletableFuture.supplyAsync(() -> {
                try {
                    return mapper.apply(t); // 执行处理函数
                } catch (Exception e) {
                    log.error("处理元素失败: {}", t, e);
                    return fallback; // 单个任务异常返回兜底值
                }
            }, executor)
            // 单个任务超时兜底
            .completeOnTimeout(fallback, timeoutMs, TimeUnit.MILLISECONDS))
            .collect(Collectors.toList());

        // 聚合所有结果
        return sequence(futures);
    }

    /**
     * 取多个任务中第一个成功的结果
     * 用于多数据源查询、多渠道调用等场景
     */
    public static <T> CompletableFuture<T> firstSuccess(List<CompletableFuture<T>> futures) {
        if (futures.isEmpty()) {
            return CompletableFuture.failedFuture(new IllegalArgumentException("任务列表为空"));
        }

        CompletableFuture<T> result = new CompletableFuture<>();

        // 每个任务完成时尝试设置结果(仅第一个成功的生效)
        futures.forEach(future -> future.whenComplete((res, ex) -> {
            if (ex == null && !result.isDone()) { // 无异常且结果未设置
                result.complete(res);
            }
        }));

        // 所有任务失败时,汇总异常
        CompletableFuture<Void> allFailed = CompletableFuture.allOf(
            futures.stream()
                .map(f -> f.exceptionally(ex -> null)) // 异常任务返回null
                .toArray(CompletableFuture[]::new)
        );

        allFailed.whenComplete((v, ex) -> {
            if (!result.isDone()) { // 所有任务均失败
                List<Throwable> causes = futures.stream()
                    .map(f -> {
                        try {
                            f.join();
                            return null;
                        } catch (CompletionException e) {
                            return e.getCause();
                        }
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
                result.completeExceptionally(
                    new AggregateException("所有任务均失败", causes)
                );
            }
        });

        return result;
    }

    /**
     * 带超时的阻塞获取结果,包装异常为RuntimeException
     * 避免调用方处理受检异常
     */
    public static <T> T getWithTimeout(CompletableFuture<T> future, long timeout, TimeUnit unit) {
        try {
            return future.get(timeout, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 恢复中断状态
            throw new RuntimeException("任务被中断", e);
        } catch (ExecutionException e) {
            throw new RuntimeException("任务执行失败", e.getCause());
        } catch (TimeoutException e) {
            throw new RuntimeException("任务超时", e);
        }
    }

    /**
     * 关闭线程池(优雅停机)
     */
    public static void shutdownExecutor(ExecutorService executor) {
        if (executor == null || executor.isShutdown()) {
            return;
        }
        executor.shutdown(); // 禁止新任务提交
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { // 等待5秒
                executor.shutdownNow(); // 强制关闭
                if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                    log.error("线程池 {} 未能正常关闭", executor);
                }
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

5.2 CFExceptionUtil:异常处理工具类

CF 的异常会被多层包装(CompletionExceptionExecutionException),获取根异常需要工具类辅助:

public final class CFExceptionUtil {

    /**
     * 获取异常链的根异常
     * 穿透 CompletionException 和 ExecutionException
     */
    public static Throwable rootCause(Throwable t) {
        if (t == null) {
            return null;
        }
        // 循环获取根异常
        while ((t instanceof CompletionException || t instanceof ExecutionException)
                && t.getCause() != null) {
            t = t.getCause();
        }
        return t;
    }

    /**
     * 判断异常链中是否包含目标异常类型
     */
    public static boolean hasCause(Throwable t, Class<? extends Throwable> targetClass) {
        if (t == null || targetClass == null) {
            return false;
        }
        Throwable root = rootCause(t);
        return targetClass.isInstance(root);
    }
}

5.3 ThreadPoolFactory:线程池工厂类

线程池配置是 CF 高性能的关键,封装工厂类统一管理:

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;

public final class ThreadPoolFactory {

    /**
     * 创建IO密集型线程池
     * 适用场景:数据库查询、接口调用、文件IO等
     * 线程数:2 * CPU核心数,队列无界(但需监控任务数)
     */
    public static ExecutorService createIOExecutor(String namePrefix) {
        int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
        int maxPoolSize = Math.max(corePoolSize, 16); // 至少16线程
        return new ThreadPoolExecutor(
            corePoolSize,
            maxPoolSize,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10000), // 较大队列
            new ThreadFactoryBuilder().setNameFormat(namePrefix + "-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝时调用者执行,避免任务丢失
        );
    }

    /**
     * 创建CPU密集型线程池
     * 适用场景:数据计算、序列化、复杂逻辑处理等
     * 线程数:CPU核心数,队列有界(防止OOM)
     */
    public static ExecutorService createCPUExecutor(String namePrefix) {
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            corePoolSize,
            corePoolSize, // 核心线程=最大线程,避免线程创建销毁
            60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000), // 有界队列
            new ThreadFactoryBuilder().setNameFormat(namePrefix + "-%d").build(),
            new ThreadPoolExecutor.AbortPolicy() // CPU任务不允许排队过多,失败需重试
        );
    }

    /**
     * 创建定时任务线程池
     * 适用场景:超时控制、定时任务等
     */
    public static ScheduledExecutorService createScheduledExecutor(String namePrefix) {
        return Executors.newScheduledThreadPool(
            2, // 定时任务线程数不需要太多
            new ThreadFactoryBuilder().setNameFormat(namePrefix + "-%d").build()
        );
    }
}

5.4 工具类使用示例

(1)用 parallelProcess 批量处理用户数据

// 批量查询用户详情
List<Long> userIds = Arrays.asList(1L, 2L, 3L, ..., 1000L);
CompletableFuture<List<UserDetail>> detailFuture = CompletableFutureUtils.parallelProcess(
    userIds,
    userId -> userService.getDetail(userId), // 单个用户查询逻辑
    500, // 单个查询超时500ms
    new UserDetail() // 超时/异常兜底值
);

// 获取结果
List<UserDetail> details = CompletableFutureUtils.getWithTimeout(detailFuture, 1, TimeUnit.SECONDS);

(2)用 firstSuccess 多数据源查询

// 多数据源查询用户信息
List<CompletableFuture<User>> futures = new ArrayList<>();
futures.add(CompletableFuture.supplyAsync(() -> cacheService.get("user:1")));
futures.add(CompletableFuture.supplyAsync(() -> dbService.queryUser(1L)));
futures.add(CompletableFuture.supplyAsync(() -> remoteService.getUser(1L)));

// 取第一个成功的结果
CompletableFuture<User> fastFuture = CompletableFutureUtils.firstSuccess(futures);
User user = fastFuture.join();

六、技术延伸:CompletableFuture 与周边生态

CF 不是孤立存在的,它与 Java 生态中的其他技术有紧密联系。了解这些关联,能让你在更复杂的场景中灵活运用 CF。

6.1 与 Spring 生态的结合

(1)@Async + CompletableFuture:声明式异步

Spring 的 @Async 注解可与 CF 结合,简化异步方法定义:

@Service
public class UserService {
    // 声明异步方法,返回CompletableFuture
    @Async("ioExecutor") // 指定线程池
    public CompletableFuture<User> queryUserAsync(Long userId) {
        return CompletableFuture.supplyAsync(() -> {
            return userDao.queryById(userId);
        });
    }
}

// 调用方
@Service
public class OrderService {
    @Autowired
    private UserService userService;

    public CompletableFuture<Order> createOrder(Long userId) {
        // 调用异步方法,链式处理
        return userService.queryUserAsync(userId)
            .thenApply(user -> new Order(user.getId(), ...));
    }
}

需要在 Spring 配置类中开启异步:

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean("ioExecutor")
    public Executor ioExecutor() {
        return ThreadPoolFactory.createIOExecutor("spring-io");
    }
}

(2)Spring WebFlux 中的 CF 支持

WebFlux 是 Spring 的响应式 Web 框架,可与 CF 互转:

// WebFlux控制器返回Mono,可由CF转换而来
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/user/{id}")
    public Mono<User> getUser(@PathVariable Long id) {
        // CF 转 Mono
        CompletableFuture<User> future = userService.queryUserAsync(id);
        return Mono.fromFuture(future);
    }
}

6.2 与反应式框架的对比:CompletableFuture vs RxJava

RxJava 是流行的反应式编程框架,与 CF 相比各有优势:

特性CompletableFutureRxJava
核心思想异步任务编排事件流响应式编程
学习曲线低(API 直观,符合 Java 习惯)高(需理解 Observable、背压等)
适用场景任务编排、批量处理持续数据流、复杂事件处理
线程控制显式指定线程池通过 subscribeOn/observeOn 控制
异常处理exceptionally/handleonError/retry 等丰富操作符
数据流处理弱(需手动组合)强(map/filter/flatMap 等操作符)

选择建议

  • 简单异步任务编排、批量处理:用 CF 足够,学习成本低;
  • 复杂事件流(如实时日志、消息推送):用 RxJava,功能更强大。

6.3 分布式系统中的 CompletableFuture

在分布式系统中,CF 可与分布式锁、消息队列结合,实现跨服务的异步协同:

(1)分布式任务编排

场景:跨服务的订单流程(订单服务→库存服务→支付服务),用 CF 串联远程调用:

public CompletableFuture<OrderResult> createDistributedOrder(OrderRequest request) {
    // 调用订单服务创建订单
    CompletableFuture<OrderDTO> orderFuture = orderClient.createOrder(request);

    // 调用库存服务锁定库存
    CompletableFuture<StockDTO> stockFuture = orderFuture.thenComposeAsync(order ->
        stockClient.lockStock(order.getProductId(), order.getQuantity())
    );

    // 调用支付服务发起支付
    CompletableFuture<PaymentDTO> payFuture = orderFuture.thenComposeAsync(order ->
        payClient.pay(order.getOrderId(), order.getAmount())
    );

    // 等待所有服务调用完成
    return CompletableFuture.allOf(stockFuture, payFuture)
        .thenApply(v -> new OrderResult(request.getOrderId(), "SUCCESS"))
        .exceptionally(ex -> {
            // 异常时回滚(调用库存解锁、订单取消接口)
            rollbackOrder(request.getOrderId());
            return new OrderResult(request.getOrderId(), "FAILED", ex.getMessage());
        });
}

(2)结合分布式锁实现异步任务幂等性

用 Redis 分布式锁确保异步任务只执行一次:

public CompletableFuture<Void> processTaskAsync(String taskId) {
    // 尝试获取分布式锁
    return CompletableFuture.supplyAsync(() ->
        redisLock.tryLock("task:" + taskId, 30, TimeUnit.SECONDS)
    ).thenComposeAsync(locked -> {
        if (!locked) {
            log.info("任务 {} 已被其他线程处理", taskId);
            return CompletableFuture.completedFuture(null);
        }
        try {
            // 执行任务
            return CompletableFuture.runAsync(() -> taskService.process(taskId));
        } finally {
            redisLock.unlock("task:" + taskId); // 释放锁
        }
    });
}

6.4 JDK 版本对 CompletableFuture 的增强

Java 8 之后的版本持续增强 CF:

  • Java 9:新增 completeOnTimeoutorTimeout 方法,简化超时控制;

    // Java 9+ 超时控制
    CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> userDao.queryById(1L))
        .completeOnTimeout(new User(), 1000, TimeUnit.MILLISECONDS); // 超时返回默认值
    
    
  • Java 12:新增 exceptionallyCompose 方法,支持异常时返回另一个 CF;

    // 异常时重试
    CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> queryUser())
        .exceptionallyCompose(ex -> CompletableFuture.supplyAsync(() -> queryUser())); // 重试一次
    
    
  • Java 19:预览特性 Structured Concurrency(结构化并发),进一步简化多任务管理。

七、避坑指南:这些错误90%的人都会犯

CF 虽然强大,但用不好容易踩坑。总结了 8 个高频错误,帮你少走弯路。

7.1 滥用默认线程池 ForkJoinPool.commonPool()

错误:直接使用 supplyAsync(Supplier) 而不指定线程池,导致所有任务共用 ForkJoinPool.commonPool()

问题commonPool 是全局共享的,线程数默认是 CPU核心数-1,IO 密集型任务会阻塞它,导致其他任务排队。

解决:始终用带线程池参数的方法(如 supplyAsync(Supplier, Executor)),按业务隔离线程池。

7.2 在回调中使用 get()join() 导致死锁

错误:在 CF 的回调中调用 get()join() 方法,可能导致死锁。

// 错误示例:回调中调用get()导致死锁
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");
future.thenRun(() -> {
    try {
        String result = future.get(); // 回调在future的线程中执行,get()会阻塞等待,导致死锁
    } catch (Exception e) { ... }
});

问题:回调可能在任务执行的线程中同步执行,此时调用 get() 会导致线程自己等自己,死锁。

解决

  • 回调中如需结果,直接用前置任务的结果(如 thenApply 的参数);
  • 必须用 get() 时,确保回调在其他线程中执行(用 thenRunAsync)。

7.3 忽略异常处理,导致问题被隐藏

错误:未用 exceptionallyhandle 处理异常,CF 的异常会被默默吃掉。

// 错误示例:未处理异常
CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("查询失败");
});
// 未调用exceptionally,异常不会被处理,调用方可能以为任务成功

问题:异常被包装在 result 中,不调用 get() 或注册异常处理,永远不会被发现。

解决:每个 CF 链都必须加 exceptionallyhandle,确保异常被处理或传递。

7.4 任务依赖循环,导致永久等待

错误:任务 A 依赖任务 B,任务 B 依赖任务 A,形成循环依赖。

// 错误示例:循环依赖
CompletableFuture<String> a = new CompletableFuture<>();
CompletableFuture<String> b = a.thenApplyAsync(s -> {
    return b.join() + "b"; // a 依赖 b
});
a.complete(b.join() + "a"); // b 依赖 a → 循环依赖,永远完不成

问题:两个任务互相等待,导致永久阻塞,浪费线程资源。

解决

  • 梳理任务依赖关系,避免循环;
  • 用独立线程池执行依赖任务,降低死锁风险。

7.5 批量任务未分片,导致线程池过载

错误:直接对 10 万条数据创建 10 万个 CF 任务,压垮线程池。

// 错误示例:大批量任务未分片
List<Long> ids = ...; // 10万个ID
List<CompletableFuture<?>> futures = ids.stream()
    .map(id -> CompletableFuture.runAsync(() -> process(id), executor))
    .collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

问题:线程池队列堆积大量任务,可能导致 OOM 或响应缓慢。

解决:分片处理,每批创建少量任务(如每批 1000 条),控制并发数。

7.6 未正确关闭线程池,导致资源泄漏

错误:创建线程池后未在应用关闭时 shutdown,导致线程泄漏。

解决

  • 在 Spring 中,将线程池定义为 @Bean 并指定 destroyMethod = "shutdown"
  • 非 Spring 应用,在 main 方法或 shutdownHook 中调用 executor.shutdown()

7.7 用 allOf 后直接 get() 所有结果,忽略异常

错误allOf 完成后,调用每个任务的 get() 时未处理异常。

// 错误示例:allOf后未处理单个任务异常
CompletableFuture<Void> allDone = CompletableFuture.allOf(future1, future2);
allDone.join();
Object res1 = future1.get(); // 如果future1异常,这里会抛ExecutionException
Object res2 = future2.get();

解决:用 join() 替代 get()join()CompletionException,非受检异常),或在 allOf 后统一处理异常。

7.8 回调逻辑过重,阻塞线程池

错误:在 thenApply 等回调中执行耗时操作(如复杂计算、同步 IO)。

// 错误示例:回调中执行耗时操作
CompletableFuture<User> future = CompletableFuture.supplyAsync(() -> queryUser());
future.thenApply(user -> {
    // 回调中执行耗时计算(CPU密集型)
    return heavyCalculation(user); // 阻塞线程池线程
});

问题:回调在指定线程池中执行,耗时操作会占用线程,降低并发能力。

解决

  • 耗时回调用 thenApplyAsync 并指定专用线程池;
  • 将重逻辑拆分为独立的 CF 任务,用 thenCompose 串联。

八、总结:CompletableFuture 是异步编程的"瑞士军刀"

从 Java 8 到 Java 21,CompletableFuture 始终是异步编程的核心工具。它的成功源于三点:

  1. 简洁的 API 设计:链式调用让异步代码像同步代码一样易读;
  2. 强大的组合能力:支持串行、并行、任一完成等多种任务关系;
  3. 高性能的底层实现:无锁 CAS + 批处理回调,在高并发场景下表现优异。

掌握 CF 不仅能提升代码性能,更能改变你的编程思维——从"串行执行"到"并行协同",从"阻塞等待"到"事件驱动"。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值