异步编程CompletableFuture的使用及应用
什么是异步编程?
异步编程是一种编程范式,它可以让程序在执行某些耗时或依赖外部资源的任务时,不阻塞当前线程,而是继续执行其他任务,从而提高程序的效率和响应性。异步编程的优点有:
- 可以充分利用多核处理器和网络资源,提高系统的吞吐量和并发能力
- 可以避免线程切换和同步的开销,减少系统的资源消耗
- 可以提升用户体验,让用户感觉程序更流畅和快速
异步编程的缺点有:
- 代码逻辑更复杂,需要处理回调、异常、状态等问题
- 调试和测试更困难,需要跟踪异步任务的执行过程和结果
- 与同步编程的习惯和模式不兼容,需要学习新的思维方式和工具
什么是CompletableFuture?
CompletableFuture是Java 8引入的一个类,它实现了Future和CompletionStage两个接口,可以用来表示一个异步计算的结果。CompletableFuture相比于传统的Future,有以下特点:
- 支持手动完成,可以在任何线程中调用complete或completeExceptionally方法来设置异步任务的结果或异常
- 支持非阻塞调用,可以在异步任务完成后执行指定的回调函数,无需等待或轮询
- 支持链式调用,可以将多个异步任务串联起来,形成一个复杂的异步流程
- 支持组合调用,可以将多个异步任务合并起来,实现并行、分支、聚合等逻辑
- 支持异常处理,可以在异步任务出现异常时执行指定的处理函数,或者将异常传递给下游的异步任务
如何使用CompletableFuture?
创建CompletableFuture
创建一个CompletableFuture对象有以下几种方式:
- 使用无参构造函数创建一个空的CompletableFuture对象,然后在适当的时机调用complete或completeExceptionally方法来设置结果或异常
- 使用静态方法runAsync或supplyAsync创建一个由指定的Runnable或Supplier执行的异步任务,并返回一个对应的CompletableFuture对象
- 使用静态方法completedFuture创建一个已经完成并包含指定结果的CompletableFuture对象
- 使用静态方法failedFuture创建一个已经完成并包含指定异常的CompletableFuture对象
例如:
// 创建一个空的CompletableFuture对象
CompletableFuture<String> future = new CompletableFuture<>();
// 创建一个由Runnable执行的异步任务,并返回一个CompletableFuture<Void>对象
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
System.out.println("Hello, world!");
});
// 创建一个由Supplier执行的异步任务,并返回一个CompletableFuture<String>对象
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello, world!";
});
// 创建一个已经完成并包含"Hello, world!"结果的CompletableFuture对象
CompletableFuture<String> future3 = CompletableFuture.completedFuture("Hello, world!");
// 创建一个已经完成并包含RuntimeException异常的CompletableFuture对象
CompletableFuture<String> future4 = CompletableFuture.failedFuture(new RuntimeException("Something went wrong"));
获取CompletableFuture的结果
获取一个CompletableFuture对象的结果有以下几种方式:
- get()方法
get()方法是最简单的一种方式,它会阻塞当前线程,直到CompletableFuture完成,并返回结果。如果CompletableFuture发生异常,get()方法会抛出ExecutionException。例如:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
String result = future.get(); // 阻塞,等待结果
System.out.println(result); // 输出Hello
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
- get(long timeout, TimeUnit unit)方法
get(long timeout, TimeUnit unit)方法和get()方法类似,但是它可以设置一个超时时间,如果在指定的时间内CompletableFuture没有完成,get()方法会抛出TimeoutException。例如:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello";
});
try {
String result = future.get(3, TimeUnit.SECONDS); // 设置超时时间为3秒
System.out.println(result); // 不会执行到这里,因为会超时
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace(); // 输出java.util.concurrent.TimeoutException
}
- join()方法
join()方法和get()方法很像,但是它不会抛出任何异常,而是直接将异常包装成CompletionException。例如:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Something went wrong"); // 模拟异常
});
String result = future.join(); // 不会抛出异常,而是返回一个CompletionException
System.out.println(result); // 输出java.util.concurrent.CompletionException: java.lang.RuntimeException: Something went wrong
- thenApply(Function<? super T,? extends U> fn)方法
thenApply(Function<? super T,? extends U> fn)方法可以用来对CompletableFuture的结果进行转换,它接受一个Function作为参数,返回一个新的CompletableFuture。例如:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> newFuture = future.thenApply(s -> s.length()); // 对结果进行转换,返回一个新的CompletableFuture
int length = newFuture.join(); // 获取转换后的结果
System.out.println(length); // 输出5
- thenAccept(Consumer<? super T> action)方法
thenAccept(Consumer<? super T> action)方法可以用来对CompletableFuture的结果进行消费,它接受一个Consumer作为参数,返回一个新的CompletableFuture。例如:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.thenAccept(s -> System.out.println(s)); // 对结果进行消费,输出Hello
- thenRun(Runnable action)方法
thenRun(Runnable action)方法可以用来在CompletableFuture完成后执行一个动作,它接受一个Runnable作为参数,返回一个新的CompletableFuture。例如:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.thenRun(() -> System.out.println("Done")); // 在完成后执行一个动作,输出Done