目录
3.1 thenApply/thenAccept/thenRun
一、介绍
Future和Callable是JDK5版本,用来获取异步处理结果。通过阻塞或轮询的方式获取结果,如:get()方法阻塞主线程,直至处理完成或超时才能获取结果。阻塞与异步编程的理念相左,同时轮询又耗费CPU资源。
CompletableFuture是JDK8版本,扩展了Future功能。提供了异步函数式编程,可以通过回调的方式获取处理结果,并且提供了不同异步间的转换和组合(异步编排)。其内部ForkJoinPool实现异步处理,可以把异步回调方式变为同步调用实现。
二、示例
1. 异步并发,阻塞/异步合并
@GetMapping("/testAsync")
@ApiOperation("测试异步调用业")
public Response<List<WcPendantTab>> testAsync(String mgdbId1, String mgdbId2, String mgdbId3){
// 返回结果
List<WcPendantTab> result = Lists.newArrayList();
try {
// 业务查询
CompletableFuture<List<WcPendantTab>> future1 = CompletableFuture.supplyAsync(() -> {
return asyncService.asyncQuery(mgdbId1);
}, executor);
CompletableFuture<List<WcPendantTab>> future2 = CompletableFuture.supplyAsync(() -> {
return asyncService.asyncQuery(mgdbId2);
}, executor);
CompletableFuture<List<WcPendantTab>> future3 = CompletableFuture.supplyAsync(() -> {
return asyncService.asyncQuery(mgdbId3);
}, executor);
// 阻塞主线程,合并查询
CompletableFuture<List<WcPendantTab>> future4 = CompletableFuture
.allOf(future1, future2, future3)
// thenApply阻塞主线程;thenApplyAsync不阻塞主线程,异步处理结果
.thenApply((Void) -> {
List<WcPendantTab> merge = Lists.newArrayList();
try {
merge.addAll(future1.get());
merge.addAll(future2.get());
merge.addAll(future3.get());
} catch (Exception e) {
e.printStackTrace();
}
return merge;
});
// 获取合并结果
result.addAll(future4.get());
} catch (Exception e) {
e.printStackTrace();
return Response.error();
}
return Response.success(result);
}
上述代码,查询数据并返回。所以future4使用thenApply来阻塞主线程获取future1、future2、future3的查询结果合并后返回。若是业务需求是不返回响应数据,可以使用thenApplyAsync不阻塞主线程,异步合并处理结果。
2. 异步并发,然后消费结果
@GetMapping("/testAsync2")
@ApiOperation("测试异步调用业2")
public Response<List<WcPendantTab>> testAsync2(String mgdbId1, String mgdbId2){
// 返回结果
List<WcPendantTab> result = Lists.newArrayList();
try {
// 业务查询
CompletableFuture<List<WcPendantTab>> future1 = CompletableFuture.supplyAsync(() -> {
return asyncService.asyncQuery(mgdbId1);
}, executor);
CompletableFuture<List<WcPendantTab>> future2 = CompletableFuture.supplyAsync(() -> {
return asyncService.asyncQuery(mgdbId2);
}, executor);
// 业务1消费业务2的结果,thenAcceptBoth阻塞主线程;thenAcceptBothAsync,不阻塞主线程
future1.thenAcceptBoth(future2, new BiConsumer<List<WcPendantTab>, List<WcPendantTab>>() {
@Override
public void accept(List<WcPendantTab> wcPendantTabs, List<WcPendantTab> wcPendantTabs2) {
wcPendantTabs.addAll(wcPendantTabs2);
}
});
// 阻塞主线程,合并查询
result.addAll(future1.get());
} catch (Exception e) {
e.printStackTrace();
return Response.error();
}
return Response.success(result);
}
上述代码,查询数据并返回。所以future1使用thenAcceptBoth来阻塞主线程获取future1、future2的查询结果,且future1使用future2的数据。若是业务需求是不返回响应数据,可以使用thenAcceptBothAsync不阻塞主线程。
三、源码解析
1. Future源码
package java.util.concurrent;
/**
* 返回异步计算的结果
* 检查计算是否完成;等待计算完成;结算完成获取结果
*/
public interface Future<V> {
// 取消任务的执行
boolean cancel(boolean mayInterruptIfRunning);
// 任务完成前,将其取消,返回true
boolean isCancelled();
// 任务正常完成,返回true
boolean isDone();
// 等待任务执行完成,获取执行结果
V get() throws InterruptedException, ExecutionException;
// 等待多久后,获取执行结果(正常完成),否则抛出异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
2. 创建CompletableFuture
2.1. supplyAsync
异步执行任务并返回处理结果,调用方通过get()或join()获取处理结果。注意:异步执行任务是非阻塞,但是获取结果get()或join()是阻塞方法。有两种方法签名,如下:
// 默认ForkJoinPool线程池执行异步任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 指定线程池执行异步任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
/**
* supplyAsync异步执行任务,并返回处理结果
*/
@Test
public void testSupplyAsync(){
CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("future.join()");
return "zyq";
}
});
System.out.println("future.join()2");
System.out.println(future.join());
}
// 执行结果
future.join()2
future.join()
get result: zyq
2.2. runAsync
异步执行任务,并没有返回结果。有两种方法签名,如下:
// 默认Fork线程池执行任务,无返回结果
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 指定线程池执行任务,无返回结果
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
/**
* runAsync异步执行任务,没有处理结果
*/
@Test
public void testRunAsync(){
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
System.out.println("future.join()");
}
});
System.out.println("future.join()2");
System.out.println("get result: " + future.join());
}
// 执行结果
future.join()2
future.join()
get result: null
2.3. completedFuture
根据已知结果作为入参,创建一个CompletableFuture。
/**
* completedFuture:根据已知结果创建一个CompletableFuture
*/
@Test
public void testCompletedFuture(){
CompletableFuture<String> future = CompletableFuture.completedFuture("zyq");
System.out.println("future.join()2");
System.out.println("get result: " + future.join());
}
// 执行结果
future.join()2
get result: zyq
3. CompletableFuture主要方法
同Future相比,CompletableFuture最大的不同是支持流式(Stream)的计算处理,多个任务之间,可以前后相连,从而形成一个计算流。比如:任务1产生的结果,可以直接作为任务2的入参,参与任务2的计算,以此类推。
3.1 thenApply/thenAccept/thenRun
a. thenApply/thenAccept/thenRun阻塞,区别:提交的任务类型不同
b. thenApplyAsync/thenAcceptAsync/thenRunAsync非阻塞
c. thenApply:Function签名(有入参和返回值),其中入参为前置任务的结果
thenAccept:Consumer签名(有入参但是没有返回值),其中入参为前置任务的结果
thenRun:Runnable签名(没有入参也没有返回值)
/**
* thenApply/thenAccept/thenRun阻塞,区别:提交的任务类型不同
* 1. thenApplyAsync/thenAcceptAsync/thenRunAsync非阻塞
* 2. thenApply:Function签名(有入参和返回值),其中入参为前置任务的结果
* thenAccept:Consumer签名(有入参但是没有返回值),其中入参为前置任务的结果
* thenRun:Runnable签名(没有入参也没有返回值)
*/
@Test
public void testThenApplyAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.completedFuture(520);
// future1结果作为future2的入参,Function<入参, 出参>
CompletableFuture<String> future2 = future1.thenApplyAsync(new Function<Integer, String>() {
@Override
public String apply(Integer param) {
System.out.println("future2.thenApplyAsync(): " + param);
return param + " zyq";
}
});
System.out.println("testThenApplyAsync()");
System.out.println("get result: " + future2.join());
}
// 执行结果
testThenApplyAsync()
future2.thenApplyAsync(): 520
get result: 520 zyq
3.2 thenCombine
thenCombine最大的不同是连接任务可以是一个独立的CompletableFuture,即:允许两个并行任务执行,最后当两个任务均完成时,再将其结果同时传递给下游任务处理。
/**
* 允许两个并行任务执行,最后当两个任务均完成时,再将其结果同时传递给下游任务处理
*/
@Test
public void testThenCombineAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("into future1");
return 520;
}
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("into future2");
return "zyq";
}
});
// future3入参是future1、future2的处理结果
CompletableFuture<String> future3 = future1.thenCombineAsync(future2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer f1, String f2) {
System.out.println("into future3");
return f1 + " " + f2;
}
});
System.out.println("testThenCombineAsync()");
System.out.println("get result: " + future3.join());
}
// 执行结果
into future2
testThenCombineAsync()
into future1
into future3
get result: 520 zyq
3.3 thenCompose
thenCombine主要用于没有前后依赖关系的任务进行连接。那么,如果两个任务之间有前后依赖关系,但是连接任务又是独立的CompletableFuture,则实现内部嵌套。
/**
* CompletableFuture内部嵌入CompletableFuture
*/
@Test
public void testThenComposeAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("into future1");
return 520;
}
});
CompletableFuture<String> future2 = future1.thenComposeAsync(new Function<Integer, CompletableFuture<String>>() {
@Override
public CompletableFuture<String> apply(Integer f1) {
System.out.println("into future2");
// 内部嵌入CompletableFuture
return CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("into future2 into CompletableFuture");
return f1 + " " + "zyq";
}
});
}
});
System.out.println("testThenComposeAsync()");
System.out.println("get result: " + future2.join());
}
// 执行结果
testThenComposeAsync()
into future1
into future2
into future2 into CompletableFuture
get result: 520 zyq
3.4 whenComplete
前置任务完成时的回调通知逻辑,当前任务没有返回值,返回前置任务的结果。注意:解决Future任务完成时,无法主动发起通知的问题。
/**
* 前置任务完成时的回调通知逻辑。
* 注意:解决Future任务完成时,无法主动发起通知的问题
*/
@Test
public void testWhenCompleteAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("into future1");
return 520;
}
});
// 返回是future1结果
// 前置任务完成时的回调通知逻辑
CompletableFuture<Integer> future2 = future1.whenCompleteAsync(new BiConsumer<Integer, Throwable>() {
@Override
public void accept(Integer f1, Throwable throwable) {
System.out.println("into future2");
// future1抛出异常
if (Objects.nonNull(throwable)){
System.out.println("into future2 is future1 failed");
}
// future1正常结果
else {
System.out.println("into future2 is future1 " + f1);
}
}
});
System.out.println("testWhenCompleteAsync()");
System.out.println("get result: " + future2.join());
}
// 执行结果
into future1
testWhenCompleteAsync()
into future2
into future2 is future1 520
get result: 520
3.5 handle
与whenComplete类似,但是有返回值,而且返回值会影响最终获取的计算结果。
/**
* 与whenComplete类似,但是有返回值
*/
@Test
public void testHandleAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("into future1");
Long.parseLong("wer");
return 520;
}
});
// 前置任务完成时的回调通知逻辑,且有返回值
CompletableFuture<String> future2 = future1.handleAsync(new BiFunction<Integer, Throwable, String>() {
@Override
public String apply(Integer f1, Throwable throwable) {
System.out.println("into future2");
// future1抛出异常
if (Objects.nonNull(throwable)){
System.out.println("into future2 is future1 failed");
return f1 + " " + "failed";
}
// future1正常结果
else {
System.out.println("into future2 is future1 " + f1);
return f1 + " " + "zyq";
}
}
});
System.out.println("testHandleAsync()");
System.out.println("get result: " + future2.join());
}
// 执行结果
into future1
testHandleAsync()
into future2
into future2 is future1 failed
get result: null failed
4. 总结
1. 方法总结
类型 | 方法 | 说明 |
创建对象 | supplyAsync | 静态方法;有返回结果 |
runAsync | 静态方法;无返回结果 | |
completedFuture | 静态方法;已知入参并返回 | |
主要方法 | thenApply /thenApplyAsync | 1. 阻塞/非阻塞; 2. Function签名(有入参,有返回值) |
thenAccept /thenAcceptAsync | 1. 阻塞/非阻塞; 2. Consumer签名(有入参,没有返回值) | |
thenRun /thenRunAsync | 1. 阻塞/非阻塞; 2. Runnable签名(没有入参,没有返回值) | |
thenCombine /thenCombineAsync | 1. 阻塞/非阻塞; 2. 连接任务是独立的CompletableFuture,即:允许两个并行任务执行,再将其结果同时传递给下游任务处理 | |
thenCompose /thenComposeAsync | 1. 阻塞/非阻塞; 2. 没有前后依赖关系的任务进行连接,实现内部嵌套 | |
whenComplete /whenCompleteAsync | 1. 阻塞/非阻塞; 2. 前置任务完成时的回调通知逻辑; 3. 当前任务没有返回值,前置任务的结果作为返回值 | |
handle /handleAsync | 1. 阻塞/非阻塞; 2. 前置任务完成时的回调通知逻辑; 3. 当前任务有返回值,影响最终结果 |
2. 测试源码
package com.cmmon.instance;
import org.junit.jupiter.api.Test;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @description 测试CompletableFuture
* @author tcm
* @version 1.0.0
* @date 2021/12/7 14:32
**/
public class CompletableFutureTest {
/**
* supplyAsync异步执行任务,并返回处理结果
*/
@Test
public void testSupplyAsync(){
CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("future.join()");
return "zyq";
}
});
System.out.println("future.join()2");
System.out.println("get result: " + future.join());
}
/**
* runAsync异步执行任务,没有处理结果
*/
@Test
public void testRunAsync(){
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
System.out.println("future.join()");
}
});
System.out.println("future.join()2");
System.out.println("get result: " + future.join());
}
/**
* completedFuture:根据已知结果创建一个CompletableFuture
*/
@Test
public void testCompletedFuture(){
CompletableFuture<String> future = CompletableFuture.completedFuture("zyq");
System.out.println("future.join()2");
System.out.println("get result: " + future.join());
}
/**
* thenApply/thenAccept/thenRun阻塞,区别:提交的任务类型不同
* 1. thenApplyAsync/thenAcceptAsync/thenRunAsync非阻塞
* 2. thenApply:Function签名(有入参和返回值),其中入参为前置任务的结果
* thenAccept:Consumer签名(有入参但是没有返回值),其中入参为前置任务的结果
* thenRun:Runnable签名(没有入参也没有返回值)
*/
@Test
public void testThenApplyAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.completedFuture(520);
// future1结果作为future2的入参,Function<入参, 出参>
CompletableFuture<String> future2 = future1.thenApplyAsync(new Function<Integer, String>() {
@Override
public String apply(Integer param) {
System.out.println("future2.thenApplyAsync(): " + param);
return param + " zyq";
}
});
System.out.println("testThenApplyAsync()");
System.out.println("get result: " + future2.join());
}
/**
* 允许两个并行任务执行,最后当两个任务均完成时,再将其结果同时传递给下游任务处理
*/
@Test
public void testThenCombineAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("into future1");
return 520;
}
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("into future2");
return "zyq";
}
});
// future3入参是future1、future2的处理结果
CompletableFuture<String> future3 = future1.thenCombineAsync(future2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer f1, String f2) {
System.out.println("into future3");
return f1 + " " + f2;
}
});
System.out.println("testThenCombineAsync()");
System.out.println("get result: " + future3.join());
}
/**
* CompletableFuture内部嵌入CompletableFuture
*/
@Test
public void testThenComposeAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("into future1");
return 520;
}
});
CompletableFuture<String> future2 = future1.thenComposeAsync(new Function<Integer, CompletableFuture<String>>() {
@Override
public CompletableFuture<String> apply(Integer f1) {
System.out.println("into future2");
// 内部嵌入CompletableFuture
return CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println("into future2 into CompletableFuture");
return f1 + " " + "zyq";
}
});
}
});
System.out.println("testThenComposeAsync()");
System.out.println("get result: " + future2.join());
}
/**
* 前置任务完成时的回调通知逻辑。
* 注意:解决Future任务完成时,无法主动发起通知的问题
*/
@Test
public void testWhenCompleteAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("into future1");
return 520;
}
});
// 返回是future1结果
// 前置任务完成时的回调通知逻辑
CompletableFuture<Integer> future2 = future1.whenCompleteAsync(new BiConsumer<Integer, Throwable>() {
@Override
public void accept(Integer f1, Throwable throwable) {
System.out.println("into future2");
// future1抛出异常
if (Objects.nonNull(throwable)){
System.out.println("into future2 is future1 failed");
}
// future1正常结果
else {
System.out.println("into future2 is future1 " + f1);
}
}
});
System.out.println("testWhenCompleteAsync()");
System.out.println("get result: " + future2.join());
}
/**
* 与whenComplete类似,但是有返回值
*/
@Test
public void testHandleAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println("into future1");
Long.parseLong("wer");
return 520;
}
});
// 前置任务完成时的回调通知逻辑,且有返回值
CompletableFuture<String> future2 = future1.handleAsync(new BiFunction<Integer, Throwable, String>() {
@Override
public String apply(Integer f1, Throwable throwable) {
System.out.println("into future2");
// future1抛出异常
if (Objects.nonNull(throwable)){
System.out.println("into future2 is future1 failed");
return f1 + " " + "failed";
}
// future1正常结果
else {
System.out.println("into future2 is future1 " + f1);
return f1 + " " + "zyq";
}
}
});
System.out.println("testHandleAsync()");
System.out.println("get result: " + future2.join());
}
}
四、参考资料
CompletableFuture 详解(一):基本概念及用法_tongtest的博客-CSDN博客_completablefuture 详解
CompletableFuture详解 - 风好大 - 博客园