HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
随笔
关于作者
  • 《从0到1学习Java多线程》

    • (一)线程是什么
    • (二)Java线程与系统线程和生命周期
    • (三)Java线程创建方式
    • (四)为什么要使用线程池
    • (五)四种线程池底层详解
    • (六)ThreadPoolExecutor自定义线程池
    • (七)线程池的大小如何确定
    • (八)Callable和Runnable的区别
    • (九)线程池异常捕获
    • (十)线程池参数——workQueue用法
    • (十一)sleep(1)、sleep(0)和sleep(1000)的区别
    • (十二)yield、notify、notifyAll、sleep、join、wait的区别
    • (十三)synchronized
    • (十四)volatile
    • (十五)ThreadLocal的使用
    • (十六)ReentrantLock可重入锁
    • (十七)AtomicInteger
    • (十八)Worker线程管理
    • CompletableFuture
  • 《从0到1搭建服务器》

  • 可观测和监控

  • 玩转IDEA

  • 03-RPC

  • 04-Spring源码

  • 05-《Java日志框架》

  • Flyme公有云平台建设
  • 专栏
  • 《从0到1学习Java多线程》
#CompletableFuture
HaC
2026-06-10
目录

CompletableFuture

# 一、是什么

  1. 定位:Java 异步编程工具类,同时实现 Future、CompletionStage 两个接口,是对原生 Future 的增强。
  2. 原生 Future 的痛点
    • 只能通过 get() 阻塞获取结果,无回调机制;
    • 无法链式编排多段异步逻辑;
    • 不能便捷组合多个异步任务(并行、等待任意 / 全部完成);
    • 异常处理繁琐,只能阻塞时捕获。
  3. CompletableFuture 核心能力
    • 支持非阻塞回调,任务完成自动执行后续逻辑,无需阻塞 / 轮询;
    • 流式链式调用,串行处理异步结果,解决回调嵌套;
    • 内置多任务组合 API:allOf、anyOf、thenCombine;
    • 完善异步异常捕获:exceptionally、handle;
    • 可手动控制任务完成:complete()、completeExceptionally(),适配回调、RPC、MQ 场景;
    • 区分同步 / 异步回调(thenXXX、thenXXXAsync),可自定义业务线程池隔离。

# 二、JDK 引入版本

  1. JDK 8 首次引入,作为 Lambda、Stream 配套的异步编程工具;
  2. JDK9 / JDK11 / JDK17 持续小幅增强 API,底层无架构改动,核心用法完全兼容 JDK8。

# 三、核心创建方式

  1. CompletableFuture.runAsync(Runnable):无返回值异步任务
  2. CompletableFuture.supplyAsync(Supplier):有返回值异步任务

不传第二个参数 Executor,默认使用 ForkJoinPool.commonPool();IO 密集业务建议传入自定义线程池,避免共用公共池耗尽。

# 四、常用分类 API

# 1. 串行处理(上一步结果作为入参)

  • thenApply:接收结果,转换并返回新值
  • thenAccept:消费结果,无返回值
  • thenRun:不依赖上一步结果,仅等待完成执行动作
  • 带 Async 后缀:后续阶段重新提交线程池异步执行

# 2. 异常处理

  • exceptionally(ex -> 兜底值):异常时返回备用数据
  • handle((res, ex) -> {}):成功、异常都会执行,同时拿到结果与异常

# 3. 多任务组合

  • thenCombine(cf, (r1,r2) -> merge):两个任务全部完成,合并两个结果
  • CompletableFuture.allOf(cf1,cf2):等待所有任务执行完毕,无返回值
  • CompletableFuture.anyOf(cf1,cf2):任意一个任务完成,立刻执行回调

# 五、获取结果两种模式(重点区分阻塞 / 非阻塞)

  1. 阻塞获取(主动等待)

    • get():抛出受检异常,需 try-catch
    • join():抛出运行时异常,无需捕获,业务更常用

    适用场景:同步 Web 接口需要组装结果返回,多任务并行缩短整体耗时。

  2. 非阻塞回调(推荐异步场景)

    全程不调用 get/join,通过链式回调自动执行,主线程无阻塞。

# 六、与 Executor + Callable + 原生 Future 核心区别

  1. Executor.submit+Callable 只是基础异步执行工具,仅能阻塞式获取结果;

    CompletableFuture 在其基础上增加了异步回调、链式编排、多任务聚合、手动完成、异步异常捕获,是一套完整的异步流程编程模型

# 七、开发注意事项

  1. IO 密集场景不要直接使用默认 ForkJoinPool,自定义线程池隔离;
  2. 异步链路必须捕获异常,否则异常只会在调用 get/join 时抛出,容易静默丢失;
  3. allOf 无返回值,如需结果需在回调内手动调用 join();
  4. 大量并发异步任务优先使用非阻塞回调,减少主线程阻塞耗时。

# 八、上代码

# Executor+Future

ExecutorService pool = Executors.newFixedThreadPool(1);
Future<String> future = pool.submit(() -> "数据");
// 必须阻塞,卡住当前线程,直到任务完成
String res = future.get();
System.out.println(res);
Future<String> f1 = pool.submit(task1);
Future<String> f2 = pool.submit(task2);
// 两处阻塞
String r1 = f1.get();
String r2 = f2.get();
String merge = r1 + r2;

痛点:主线程阻塞;如果要做后续处理,必须等 get 返回,无法异步回调

# CompletableFuture

执行完成回调:

CompletableFuture.supplyAsync(() -> "数据")
        .thenApply(s -> s + "后缀") // 任务完成自动执行,不阻塞主线程
        .thenAccept(System.out::println);
// 主线程可以继续干别的,不用等待

合并任务,都完成后才执行回调anyOf:

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(task1);
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(task2);
// 非阻塞自动合并
cf1.thenCombine(cf2, (a,b) -> a+b).thenAccept(System.out::println);

合并任务,任意一个任务完成就执行回调 anyOf:

CompletableFuture<Object> any = CompletableFuture.anyOf(cf1, cf2);
any.thenAccept(res -> System.out.println("任意任务完成:" + res));

# 九、疑问

# 关键区分:阻塞发生在哪

  1. 主线程直接调用 cf.join () /cf.get ()→ 主线程阻塞,卡住后续代码,和原始 Future 无差别;

  2. 在 thenAccept /thenApply/thenCombine 等回调内部调用 join ()→ 阻塞的是回调执行线程

    (线程池工作线程),主线程早已释放,不会影响主流程;

  3. 全程只用链式回调,不写任何 get/join→ 全程无阻塞,纯异步事件驱动。

# Web接口阻塞场景

Web 接口(SpringMVC/SpringBoot)必须同步返回数据给前端,响应流程是同步模型:

@GetMapping("/query")
public String query() {
    CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(task1);
    CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(task2);
    // 接口要同步返回,只能阻塞等待所有异步任务结果
    return cf1.join() + cf2.join();
}

这种场景阻塞不可避免,但异步并行查询缩短整体耗时:

  • 串行执行:耗时 = task1 耗时 + task2 耗时

  • CompletableFuture 并行:耗时 = max (task1 耗时,task2 耗时)

    阻塞只是等最慢的那个任务,整体性能大幅提升。

阅读全文
×

(为防止恶意爬虫)
扫码或搜索:HelloCoder
发送:290992
即可永久解锁本站全部文章

解锁
#CompletableFuture
上次更新: 2026-06-10 17:28:52
(十八)Worker线程管理
(1)选择云服务器

← (十八)Worker线程管理 (1)选择云服务器→

最近更新
01
MySQL支持的锁有哪些
06-10
02
HTTP 是不保存状态的协议, 如何保存用户状态
06-10
03
网络层常见的协议
06-10
更多文章>
Theme by Vdoing | Copyright © 2020-2026 HaC
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式