CompletableFuture
是 Java 8 引入的一个强大的异步编程工具,位于 java.util.concurrent
包中。它是 Future
接口的实现类,但相比传统的 Future
,CompletableFuture
提供了更灵活、更强大的异步任务处理能力,支持链式调用、组合多个异步任务、异常处理等功能,极大地简化了 Java 中异步编程的复杂性。
一、CompletableFuture
的作用
CompletableFuture
主要用于:
- 异步执行任务:可以在另一个线程中执行耗时操作,而不会阻塞当前线程。
- 任务组合:可以将多个异步任务组合起来,比如:一个任务依赖另一个任务的结果、多个任务并行执行后合并结果等。
- 异常处理:提供了丰富的 API 来处理异步任务中可能发生的异常。
- 回调机制:支持在任务完成后执行回调操作,无需手动阻塞等待结果。
这里主要介绍第一种用法(最近优化接口响应速度用到了这个方法,其他暂时没有用过,等以后再补充):异步执行任务
二、用法
异步执行可以通过静态方法(如 supplyAsync
和 runAsync
)将任务提交到线程池中异步执行。
-
CompletableFuture.supplyAsync(Supplier<U> supplier)
异步执行一个有返回值的任务(Supplier
),返回一个CompletableFuture<U>
。 -
CompletableFuture.runAsync(Runnable runnable)
异步执行一个没有返回值的任务(Runnable
),返回一个CompletableFuture<Void>
。
默认情况下,supplyAsync
和 runAsync
使用的是 ForkJoinPool.commonPool()
作为线程池。如果需要指定线程池,可以传入第二个参数,例如:CompletableFuture.supplyAsync(() -> ..., executor);
三、普通顺序执行和CompletableFuture
异步执行对比
顺序执行:
public class SyncExample {
// 模拟从服务获取用户信息
public static String getUserInfo() {
try {
// 模拟网络延迟
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "UserInfo";
}
// 模拟从服务获取订单信息
public static String getOrderInfo() {
try {
// 模拟网络延迟
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "OrderInfo";
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis(); // 记录开始时间
// 依次调用两个服务
String userInfo = getUserInfo(); // 耗时 2 秒
String orderInfo = getOrderInfo(); // 耗时 3 秒
// 合并结果
String result = userInfo + " + " + orderInfo;
long endTime = System.currentTimeMillis(); // 记录结束时间
System.out.println("同步方式结果: " + result);
System.out.println("同步方式耗时: " + (endTime - startTime) + " 毫秒");
}
}
执行结果:
CompletableFuture
异步执行:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class AsyncExample {
// 模拟从服务获取用户信息
public static String getUserInfo() {
try {
// 模拟网络延迟
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "UserInfo";
}
// 模拟从服务获取订单信息
public static String getOrderInfo() {
try {
// 模拟网络延迟
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "OrderInfo";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
long startTime = System.currentTimeMillis(); // 记录开始时间
// 异步调用 getUserInfo()
CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo());
// CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo(),executor);可以指定线程池
// 异步调用 getOrderInfo()
CompletableFuture<String> orderFuture = CompletableFuture.supplyAsync(() -> getOrderInfo());
// 等待两个任务都完成,并合并结果
CompletableFuture<String> combinedFuture = userFuture.thenCombine(orderFuture, (userInfo, orderInfo) -> {
return userInfo + " + " + orderInfo;
});
// 获取最终结果(阻塞直到任务完成)
String result = combinedFuture.get();
long endTime = System.currentTimeMillis(); // 记录结束时间
System.out.println("异步方式结果: " + result);
System.out.println("异步方式耗时: " + (endTime - startTime) + " 毫秒");
}
}
执行结果
getUserInfo()
和 getOrderInfo()
是异步执行的,它们会分别在不同的线程中运行,互不阻塞。程序会同时发起这两个任务的调用,而不是等待一个完成后再开始另一个。由于两个任务是并行执行的,总耗时取决于耗时最长的那个任务,即 3 秒
可以指定自定义的线程池:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
ExecutorService executor = Executors.newFixedThreadPool(2); // 创建一个固定大小的线程池
CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> getUserInfo(), executor);
CompletableFuture<String> orderFuture = CompletableFuture.supplyAsync(() -> getOrderInfo(), executor);