在Java开发中,异步编程是提升性能的重要手段,但传统的Future
和回调方式容易导致**“回调地狱”,代码难以维护。Java 8引入的CompletableFuture
让异步编程变得优雅且强大!本文将带你从入门到精通**,彻底掌握CompletableFuture
的使用方法,助你写出高效、易读的异步代码!
在 Java 5 引入的Future
虽然能表示异步任务,但存在以下问题:
future.get()
会阻塞线程,影响性能。ExecutorService executor = Executors.newFixedThreadPool(2);
Future future1 = executor.submit(() -> fetchDataFromDB());
Future future2 = executor.submit(() -> processData(future1.get())); // 阻塞!
Future future3 = executor.submit(() -> saveData(future2.get())); // 又阻塞!
这样的代码不仅难读,还容易阻塞线程!
✅ 非阻塞:支持回调,无需手动调用get()
✅ 链式调用:可组合多个异步任务
✅ 异常处理:提供exceptionally
等方法
✅ 超时控制:支持orTimeout
和completeOnTimeout
CompletableFuture
实现了Future
和CompletionStage
接口,代表一个异步计算的结果,可以:
// 1. 直接创建已完成的任务
CompletableFuture completedFuture = CompletableFuture.completedFuture("Hello");
// 2. 异步执行任务(默认使用 ForkJoinPool)
CompletableFuture asyncTask = CompletableFuture.runAsync(() -> {
System.out.println("Running in background...");
});
// 3. 异步执行带返回值的任务
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
return "Result from async task";
});
方法 | 返回值 | 适用场景 |
---|---|---|
runAsync(Runnable) |
CompletableFuture |
无返回值的任务 |
supplyAsync(Supplier) |
CompletableFuture |
有返回值的任务 |
默认使用ForkJoinPool.commonPool()
,但可自定义线程池:
ExecutorService customExecutor = Executors.newFixedThreadPool(4);
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
return "Custom thread pool";
}, customExecutor); // 使用自定义线程池
CompletableFuture future = CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World") // 转换结果
.thenApply(String::toUpperCase); // 再次转换
System.out.println(future.get()); // 输出 "HELLO WORLD"
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(s -> System.out.println("Received: " + s)); // 消费结果但不返回
CompletableFuture.supplyAsync(() -> "Hello")
.thenRun(() -> System.out.println("Task finished!")); // 无参数,无返回值
CompletableFuture future = CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World")); // 返回新的 Future
CompletableFuture future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2)
.thenAccept(System.out::println); // 输出 "Hello World"
// 等待所有任务完成
CompletableFuture all = CompletableFuture.allOf(future1, future2, future3);
// 任意一个完成即可
CompletableFuture any = CompletableFuture.anyOf(future1, future2);
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) throw new RuntimeException("Oops!");
return "Success";
}).exceptionally(ex -> {
System.out.println("Error: " + ex.getMessage());
return "Fallback";
}).thenAccept(System.out::println);
future.handle((result, ex) -> {
if (ex != null) return "Fallback";
return result.toUpperCase();
});
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(2000); } catch (InterruptedException e) {}
return "Result";
}).orTimeout(1, TimeUnit.SECONDS); // 1秒后超时
future.exceptionally(ex -> "Timeout!"); // 捕获TimeoutException
future.completeOnTimeout("Default", 1, TimeUnit.SECONDS);
CompletableFuture api1 = fetchFromAPI1();
CompletableFuture api2 = fetchFromAPI2();
CompletableFuture.allOf(api1, api2)
.thenRun(() -> {
String result1 = api1.join();
String result2 = api2.join();
System.out.println("Combined: " + result1 + result2);
});
CompletableFuture.supplyAsync(() -> queryFromDB())
.thenApplyAsync(data -> updateCache(data)) // 异步更新缓存
.thenAccept(data -> sendToClient(data)); // 返回给客户端
CompletableFuture 核心优势:
✔ 非阻塞:告别future.get()
阻塞
✔ 链式调用:优雅组合多个异步任务
✔ 异常处理:exceptionally
、handle
等
✔ 超时控制:orTimeout
、completeOnTimeout
最佳实践:
supplyAsync
/runAsync
启动异步任务thenApply
、thenCompose
等链式调用现在就去重构你的异步代码吧!
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
HTTP、HTTPS、Cookie 和 Session 之间的关系
什么是 Cookie?简单介绍与使用方法
什么是 Session?如何应用?
使用 Spring 框架构建 MVC 应用程序:初学者教程
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
如何理解应用 Java 多线程与并发编程?
把握Java泛型的艺术:协变、逆变与不可变性一网打尽
Java Spring 中常用的 @PostConstruct 注解使用总结
如何理解线程安全这个概念?
理解 Java 桥接方法
Spring 整合嵌入式 Tomcat 容器
Tomcat 如何加载 SpringMVC 组件
“在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”
“避免序列化灾难:掌握实现 Serializable 的真相!(二)”
如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)
解密 Redis:如何通过 IO 多路复用征服高并发挑战!
线程 vs 虚拟线程:深入理解及区别
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
“打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
Java 中消除 If-else 技巧总结
线程池的核心参数配置(仅供参考)
【人工智能】聊聊Transformer,深度学习的一股清流(13)
Java 枚举的几个常用技巧,你可以试着用用
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
HTTP、HTTPS、Cookie 和 Session 之间的关系
使用 Spring 框架构建 MVC 应用程序:初学者教程
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
Java Spring 中常用的 @PostConstruct 注解使用总结
线程 vs 虚拟线程:深入理解及区别
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)