【Java从入门到入土】32:CompletableFuture:异步编程的未来
【Java从入门到入土】32:CompletableFuture:异步编程的未来
在Java编程中,“异步”是提升程序吞吐量、避免线程阻塞的核心手段。传统同步编程中,线程执行任务时会“原地等待”结果返回(如IO操作、远程调用),导致CPU资源闲置;而异步编程让线程在等待期间处理其他任务,大幅提升资源利用率。CompletableFuture作为JDK8引入的异步编程利器,解决了传统Future的诸多痛点,成为Java异步编程的“标准答案”。今天从异步编程的必要性、Future的局限、CompletableFuture的核心用法三个维度,讲透异步编程的实现逻辑。
🤔 为什么需要异步编程?
同步编程的核心问题是“阻塞”——当线程执行耗时操作(如数据库查询、网络请求、文件读写)时,会进入等待状态,直到操作完成才能继续执行。这种模式在高并发场景下会导致:
- 资源浪费:大量线程阻塞等待,CPU无法充分利用;
- 响应缓慢:单线程同步执行多个耗时任务,整体响应时间等于所有任务耗时之和;
- 扩展性差:增加线程数会带来上下文切换开销,无法线性提升性能。
异步编程的核心思想是“非阻塞”:发起耗时任务后,线程不等待结果,而是继续执行其他任务;当耗时任务完成后,再通过回调/通知机制处理结果。例如:
- 同步:
查询数据库 → 等待结果 → 处理结果(总耗时=查询耗时+处理耗时); - 异步:
发起数据库查询(异步) → 执行其他任务 → 数据库返回结果后处理(总耗时=max(查询耗时, 其他任务耗时)+处理耗时)。
🚫 Future的局限性:只能获取结果
JDK5引入的Future是最早的异步编程工具,它代表“异步任务的未来结果”,可以通过get()获取任务结果,但存在致命局限:
- 阻塞获取结果:
get()方法是阻塞的,调用后线程必须等待任务完成; - 无回调机制:无法在任务完成后自动触发后续处理,只能主动轮询/阻塞;
- 无法组合任务:多个
Future无法链式执行、并行执行,也无法处理依赖关系; - 异常处理繁琐:需要手动捕获
ExecutionException,无法优雅处理异步任务异常。
Future基础用法(暴露问题)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureLimitTest {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
// 异步执行耗时任务
Future<String> future = executor.submit(() -> {
Thread.sleep(2000); // 模拟耗时操作
return "异步任务结果";
});
// 1. 阻塞获取结果(主线程等待2秒)
System.out.println("等待异步任务结果...");
String result = future.get(); // 阻塞!
System.out.println("获取结果:" + result);
executor.shutdown();
}
}
上述代码中,future.get()会让主线程阻塞2秒,完全失去了异步编程的意义——这也是Future几乎无法在生产环境单独使用的原因。
✨ CompletableFuture:更强大的异步工具
CompletableFuture实现了Future和CompletionStage接口,既保留了Future的核心能力,又新增了链式调用、任务组合、异常处理等核心特性,真正实现“非阻塞异步编程”。
1. 核心优势
| 特性 | Future | CompletableFuture |
|---|---|---|
| 获取结果 | 仅阻塞get() | 支持非阻塞回调(thenAccept等) |
| 任务组合 | 不支持 | 支持链式、并行、依赖组合 |
| 异常处理 | 手动捕获异常 | 内置exceptionally/handle等方法 |
| 异步执行 | 依赖线程池手动提交 | 内置异步执行方法(supplyAsync等) |
2. 基础创建方式
CompletableFuture提供了静态方法快速创建异步任务,无需手动创建线程池(也可自定义线程池):
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureCreateTest {
public static void main(String[] args) throws Exception {
// 1. 无返回值的异步任务(runAsync)
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
System.out.println("无返回值异步任务:" + Thread.currentThread().getName());
});
// 2. 有返回值的异步任务(supplyAsync)
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "有返回值异步任务结果";
});
// 3. 自定义线程池(推荐,避免使用默认ForkJoinPool)
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
return "自定义线程池执行的任务";
}, executor);
// 非阻塞获取结果(回调方式)
future2.thenAccept(result -> System.out.println("获取future2结果:" + result));
// 等待所有任务完成(避免主线程提前退出)
CompletableFuture.allOf(future1, future2, future3).join();
executor.shutdown();
}
}
🔗 链式调用:thenApply、thenAccept、thenRun
CompletableFuture的核心能力是“链式调用”——异步任务完成后,自动触发后续处理,无需手动阻塞。根据后续处理是否需要参数/返回值,分为三类核心方法:
| 方法 | 入参 | 返回值 | 说明 |
|---|---|---|---|
| thenApply | 上一步结果 | 新的CompletableFuture | 处理结果并返回新值 |
| thenAccept | 上一步结果 | Void | 消费结果,无返回值 |
| thenRun | 无(仅执行动作) | Void | 仅执行操作,不依赖结果 |
链式调用示例
import java.util.concurrent.CompletableFuture;
public class CompletableFutureChainTest {
public static void main(String[] args) {
// 链式调用:异步任务 → 处理结果 → 消费结果 → 执行收尾操作
CompletableFuture.supplyAsync(() -> {
// 第一步:异步获取数据
return 100;
}).thenApply(num -> {
// 第二步:处理结果(乘以2)
return num * 2;
}).thenAccept(result -> {
// 第三步:消费结果(打印)
System.out.println("最终结果:" + result); // 输出200
}).thenRun(() -> {
// 第四步:执行收尾操作
System.out.println("异步任务链执行完成");
}).join(); // 等待整个链完成
}
}
异步执行的链式调用
默认情况下,链式方法会使用上一步任务的线程执行,若需异步执行,使用带Async后缀的方法(thenApplyAsync、thenAcceptAsync等):
CompletableFuture.supplyAsync(() -> {
System.out.println("第一步线程:" + Thread.currentThread().getName());
return 100;
}).thenApplyAsync(num -> {
System.out.println("第二步线程(异步):" + Thread.currentThread().getName());
return num * 2;
}).join();
🧩 组合多个异步任务:thenCombine、allOf、anyOf
实际开发中,常需要处理多个异步任务的依赖或并行关系,CompletableFuture提供了丰富的组合方法:
1. thenCombine:两个任务完成后合并结果
适用于“两个异步任务独立执行,完成后合并结果”的场景:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureCombineTest {
public static void main(String[] args) {
// 任务1:获取商品价格
CompletableFuture<Integer> priceFuture = CompletableFuture.supplyAsync(() -> 100);
// 任务2:获取折扣率
CompletableFuture<Double> discountFuture = CompletableFuture.supplyAsync(() -> 0.8);
// 合并两个任务结果(计算最终价格)
CompletableFuture<Double> finalPriceFuture = priceFuture.thenCombine(discountFuture, (price, discount) -> {
return price * discount;
});
finalPriceFuture.thenAccept(finalPrice -> System.out.println("最终价格:" + finalPrice)); // 输出80.0
}
}
2. allOf:等待所有任务完成(无返回值)
适用于“多个异步任务并行执行,全部完成后执行后续操作”的场景:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureAllOfTest {
public static void main(String[] args) {
// 任务1:查询用户信息
CompletableFuture<Void> userFuture = CompletableFuture.runAsync(() -> System.out.println("查询用户信息"));
// 任务2:查询订单信息
CompletableFuture<Void> orderFuture = CompletableFuture.runAsync(() -> System.out.println("查询订单信息"));
// 任务3:查询商品信息
CompletableFuture<Void> goodsFuture = CompletableFuture.runAsync(() -> System.out.println("查询商品信息"));
// 等待所有任务完成
CompletableFuture<Void> allFuture = CompletableFuture.allOf(userFuture, orderFuture, goodsFuture);
allFuture.thenRun(() -> System.out.println("所有查询任务完成")).join();
}
}
3. anyOf:任意一个任务完成即可
适用于“多个异步任务并行执行,只要有一个完成就处理结果”的场景(如多源数据查询,取最快的结果):
import java.util.concurrent.CompletableFuture;
public class CompletableFutureAnyOfTest {
public static void main(String[] args) {
// 任务1:从阿里云获取数据(耗时2秒)
CompletableFuture<String> aliFuture = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(2000); } catch (InterruptedException e) {}
return "阿里云数据";
});
// 任务2:从腾讯云获取数据(耗时1秒)
CompletableFuture<String> tencentFuture = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "腾讯云数据";
});
// 任意一个任务完成就处理结果
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(aliFuture, tencentFuture);
anyFuture.thenAccept(result -> System.out.println("获取到最快的结果:" + result)); // 输出“腾讯云数据”
}
}
🚨 异常处理:exceptionally、handle
异步任务的异常处理是重中之重,CompletableFuture提供了两种核心异常处理方式:
1. exceptionally:异常时返回默认值
适用于“任务异常时,返回兜底结果”的场景:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExceptionallyTest {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟异常
throw new RuntimeException("异步任务执行失败");
}).exceptionally(ex -> {
// 异常时返回默认值
System.out.println("捕获异常:" + ex.getMessage());
return "默认兜底结果";
});
future.thenAccept(result -> System.out.println("最终结果:" + result)); // 输出“默认兜底结果”
}
}
2. handle:正常/异常都处理
适用于“无论任务是否异常,都统一处理结果/异常”的场景:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureHandleTest {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟正常执行(注释下面一行,模拟异常)
// throw new RuntimeException("执行失败");
return "正常结果";
}).handle((result, ex) -> {
if (ex != null) {
System.out.println("异常:" + ex.getMessage());
return "异常兜底结果";
} else {
return "处理后的结果:" + result;
}
});
future.thenAccept(System.out::println);
}
}
🎯 核心总结
CompletableFuture彻底解决了传统Future的局限性,是Java异步编程的核心工具,核心要点如下:
- 异步编程的意义:避免线程阻塞,提升CPU利用率和程序响应速度;
- 核心创建方式:
runAsync(无返回值)、supplyAsync(有返回值),推荐自定义线程池; - 链式调用:
thenApply:处理结果并返回新值;thenAccept:消费结果(无返回值);thenRun:仅执行操作(不依赖结果);- 带
Async后缀的方法异步执行后续任务;
- 任务组合:
thenCombine:合并两个任务的结果;allOf:等待所有任务完成;anyOf:任意一个任务完成即可;
- 异常处理:
exceptionally:异常时返回兜底值;handle:统一处理正常/异常场景。
掌握CompletableFuture,你就能优雅地实现异步编程,处理多任务并行、依赖、异常等复杂场景——无论是接口响应优化、多源数据聚合,还是高并发任务调度,都能找到最优解。
更多推荐
所有评论(0)