以下内容参考《Java并发编程深度解析与实战》谭锋(第11章)
为什么要用 CompletableFuture
使用 CompletableFuture 是因为 Future 虽然可以获取线程执行结果,但是无法使用回调函数,只能依靠阻塞方法 get() 来获取线程结果,CompletableFuture 就是对 Future 的优化和增强,具体可以实现以下功能
- 提供了类似
Future的阻塞式获取结果和状态的方法 - 提供
CompletionStage任务执行之后回调 - 多个异步任务的聚合,串行,并行等功能
对于 Future 的使用可以参考 [[Future的使用]]
构造异步方法
CompletableFuture 提供了 4 个静态方法来构造一个异步事件
// 含有返回值的异步方法(自定义线程池和默认线程池)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
// 不含有返回值的异步方法(自定义线程池和默认线程池)
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
supplyAsync 示例
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
return 1;
});
System.out.println("线程执行结果:" + future.get());
System.out.println("主线程:" + Thread.currentThread().getName());
}
当前线程:ForkJoinPool.commonPool-worker-1
线程执行结果:1
主线程:main
tab runAsync示例
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
});
System.out.println("线程执行结果:" + future.get());
System.out.println("主线程:" + Thread.currentThread().getName());
}
当前线程:ForkJoinPool.commonPool-worker-1
线程执行结果:null
主线程:main
任务与和或的静态方法
CompletableFuture 中还有另外两个特殊的静态方法
// 接收多个CompletableFuture无返回值任务,当所有的CompletableFuture任务执行结束后,返回一个新的CompletableFuture对象
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
//接收多个CompletableFuture带有返回值任务,当任何一个CompletableFuture任务执行完成后,返回一个新的CompletableFuture对象
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
allOff 代码示例
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("当前线程:" + Thread.currentThread().getName());
});
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("当前线程:" + Thread.currentThread().getName());
});
LocalTime now = LocalTime.now();
System.out.println("before time:" + now.getMinute() + ":" + now.getSecond());
CompletableFuture.allOf(future1, future2).join();
now = LocalTime.now();
System.out.println("after time:" + now.getMinute() + ":" + now.getSecond());
}
执行结果,用了5s才返回,因为要所有都执行完成才返回,就需要看耗时最长的那个线程
before time:50:33
当前线程:ForkJoinPool.commonPool-worker-1
当前线程:ForkJoinPool.commonPool-worker-2
after time:50:38
anyOf()代码示例
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("当前线程:" + Thread.currentThread().getName());
});
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("当前线程:" + Thread.currentThread().getName());
});
LocalTime now = LocalTime.now();
System.out.println("before time:" + now.getMinute() + ":" + now.getSecond());
CompletableFuture.anyOf(future1, future2).join();
now = LocalTime.now();
System.out.println("after time:" + now.getMinute() + ":" + now.getSecond());
}
执行结果,只用了3s就返回了,因为是其中一个执行完成就行,所以看耗时最短的那个线程
before time:53:43
当前线程:ForkJoinPool.commonPool-worker-1
after time:53:46
CompletionStage 详解
CompletionStage表示任务执行的一个阶段,每个异步任务都会返回一个新的CompletionStage对象- 可以针对多个
CompletionStage对象进行串行、并行或者聚合来进行下一阶段的操作
CompletionStage 类中提供了很多方法来实现多个 CompletionStage 的串行,并行等功能,可以按照如下方式进行分类
flowchart LR
A[CompletionStage]
B["纯消费性方法 \n (上一个异步结果 \n 作为当前方法的参数进行计算 \n 都含有 Accept 关键字)"]
B1[依赖单个 CompletionStage任务完成]
B11["thenAccept(Consumer)"]
B12["thenAcceptAsync(Consumer)"]
B13["thenAcceptAsync(Consumer,Executor)"]
B1-->B11 & B12 & B13
B2[依赖两个 CompletionStage任务完成]
B21["thenAcceptBoth(CompletionStage,Consumer)"]
B22["thenAcceptBothAsync(CompletionStage,Consumer)"]
B23["thenAcceptBothAsync(CompletionStage,Consumer,Executor)"]
B2-->B21 & B22 & B23
B3[依赖两个 CompletionStage任务中任何一个完成]
B31["acceptEither(CompletionStage,Consumer)"]
B32["acceptEitherAsync(CompletionStage,Consumer)"]
B33["acceptEitherAsync(CompletionStage,Consumer,Executor)"]
B3-->B31 & B32 & B33
B-->B1 & B2 & B3
C["有返回值类型的方法 \n (上一个异步结果 \n 作为当前方法的参数进行计算 \n 并且会产生新的有返回值\n的CompletionStage对象)"]
C1[依赖单个 CompletionStage任务完成]
C11["thenApply(Function)"]
C12["thenApplyAsync(Function)"]
C13["thenApplyAsync(Function,Executor)"]
C1-->C11 & C12 & C13
C2[依赖两个 CompletionStage任务完成]
C21["thenCombine(CompletionStage,BiFunction)"]
C22["thenCombineAsync(CompletionStage,BiFunction)"]
C23["thenCombineAsync(CompletionStage,BiFunction,Executor)"]
C2-->C21 & C22 & C23
C3[依赖两个 CompletionStage任务中任何一个完成]
C31["acceptToEither(CompletionStage,Function)"]
C32["acceptToEitherAsync(CompletionStage,Function)"]
C33["acceptToEitherAsync(CompletionStage,Function,Executor)"]
C3-->C31 & C32 & C33
C-->C1 & C2 & C3
D["不消费也没有返回值类型的方法\n不依赖上个阶段的结果\n上一个阶段完成就执行指定的任务\n这类方法都包含 run 关键字"]
D1[依赖单个 CompletionStage任务完成]
D11["thenRun(Runnable)"]
D12["thenRunAsync(Runnable)"]
D13["thenRunAsync(FuncRunnabletion,Executor)"]
D1-->D11 & D12 & D13
D2[依赖两个 CompletionStage任务完成]
D21["runAfterBoth(CompletionStage,Runnable)"]
D22["runAfterBothAsync(CompletionStage,Runnable)"]
D23["runAfterBothAsync(CompletionStage,Runnable,Executor)"]
D2-->D21 & D22 & D23
D3[依赖两个 CompletionStage任务中任何一个完成]
D31["runAfterEither(CompletionStage,Runnable)"]
D32["runAfterEitherAsync(CompletionStage,Runnable)"]
D33["runAfterEitherAsync(CompletionStage,Runnable,Executor)"]
D3-->D31 & D32 & D33
D-->D1 & D2 & D3
E[组合类型的方法]
E1["thenCompose(Function,CompletionStage)"]
E2["thenComposeAsync(Function,CompletionStage)"]
E3["thenComposeAsync(Function,CompletionStage,Executor)"]
E-->E1 & E2 & E3
A-->B & C & D & E
纯消费型
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("thread main:" + Thread.currentThread().getName());
CompletableFuture.supplyAsync(()->{
System.out.println("first stage:" + Thread.currentThread().getName());
return 1;
}).thenAccept((Integer v)-> {
// v 就是第一步返回的结果
System.out.println("then accept thread:" + Thread.currentThread().getName());
System.out.println(v + 1); // 2
}).join();
}
thread main:main
first stage:ForkJoinPool.commonPool-worker-1
then accept thread:main
2
上面代码调用 thenAccept() 使用的是 main 线程,如果是使用 thenAcceptAsync(), 调用结果如下
thread main:main
first stage:ForkJoinPool.commonPool-worker-1
then accept thread:ForkJoinPool.commonPool-worker-1
2
有返回值类型
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("thread main:" + Thread.currentThread().getName());
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
// 第一个阶段任务返回的是 int 类型
return 1;
}).thenCombineAsync(CompletableFuture.supplyAsync(() -> {
// 第二个阶段任务返回的是 String 类型
return "2";
}), (Integer i1, String s1) -> {
return i1 + s1;
});
System.out.println(completableFuture.get()); // 12
}
不消费也没有返回值类型
public static void main(String[] args) throws Exception {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
CompletableFuture.supplyAsync(() -> {
try {
// 第一个阶段任务等待4s
Thread.sleep(4000);
System.out.println("第一个阶段:" + LocalDateTime.now().format(formatter));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
}).runAfterEitherAsync(CompletableFuture.supplyAsync(() -> {
// 第二个阶段任务等待2s
try {
Thread.sleep(2000);
System.out.println("第二个阶段:" + LocalDateTime.now().format(formatter));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "2";
}), () -> {
System.out.println("最终:" + LocalDateTime.now().format(formatter));
System.out.println("最终执行的任务");
}).join();
}
执行结果
第二个阶段:2023-11-20 23:56:32
最终:2023-11-20 23:56:32
最终执行的任务
组合类型
thenCompose()是多任务组合方法,它的作用是把两个CompletionStage任务进行组合达到串行执行的目的,也就是把第一个任务的执行结果作为参数传递给第二个任务执行,它有点类似于前面提到的thenCombine()方法,最大的不同在于thenCompose()方法中的任务存在先后关系,而thenCombine()中两个任务是并行执行的
异常处理
CompletionStage 是链式处理,当前面的任务出现异常的时候,会导致后面的任务无法处理
public static void main(String[] args) throws Exception {
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("第一个任务出现异常");
}).thenRun(()->{
// 这里并不会打印,因为前面一个任务出现了异常,导致该任务无法执行
System.out.println("第二个任务");
}).join();
}
所以在 CompletionStage 中也提供了异常处理的相关方法,主要有以下三类
flowchart LR
A[CompletionStage异常处理]
B["以whenComplete前缀开头的方法\n不论前置的CompletionStage任务是正常执行结束\n还是出现异常,都能够触发特定的action\n这些方法都接收两个参数,\n一个是正常的结果,一个是异常\n没有异常时第二个就是null"]
B1["whenComplete(BiConsumer)"]
B2["whenCompleteAsync(BiConsumer)"]
B3["whenCompleteAsync(BiConsumer,Executor)"]
B-->B1 & B2 & B3
C["以handle前缀开头的方法\n表示前置任务执行完成后,\n不管前置任务执行状态是正常还是异常,\n都会执行其中的函数fn,\n它和whenComplete类方法的\n作用几乎一致,不同点在于,\n这类方法是有返回值类型的方法"]
C1["handle(BiFunction)"]
C2["handleAsync(BiFunction)"]
C3["handleAsync(BiFunction,Executor)"]
C-->C1 & C2 & C3
D["exceptionally()"]
D1["exceptionally()方法接收一个函数fn,\n当上一个CompletionStage出现异常时,\n会把该异常作为参数传递给函数fn。\n该方法有一个CompletionStage的返回值,\n说明当前方法可以在接收到\n上一个阶段的异常时进行进一步处理,\n返回一个新的CompletionStage对象实例"]
D-->D1
A-->B & C & D
whenComplete类型方法
没有异常的情况
public static void main(String[] args) throws Exception {
CompletableFuture.supplyAsync(() -> {
return 1;
}).whenCompleteAsync((result, excep)->{
System.out.println("任务结果:" + result);
System.out.println("第一个任务的异常:" + excep);
}).join();
}
执行结果
任务结果:1
第一个任务的异常:null
有异常的情况
public static void main(String[] args) throws Exception {
CompletableFuture.supplyAsync(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
throw new RuntimeException("第一个任务出现异常");
}
return 1;
}).whenCompleteAsync((result, excep)->{
System.out.println("任务结果:" + result);
System.out.println("第一个任务的异常:" + excep);
}).join();
}
执行结果
任务结果:null
第一个任务的异常:java.util.concurrent.CompletionException: java.lang.RuntimeException: 第一个任务出现异常
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.RuntimeException: 第一个任务出现异常
handle 类型方法
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
throw new RuntimeException("第一个任务出现异常");
}
return 1;
}).handle((result, excep) -> {
System.out.println("任务结果:" + result);
System.out.println("第一个任务的异常:" + excep.toString().substring(1, 70));
return "第二个任务的结果";
});
// 最终的执行结果
System.out.println(completableFuture.get());
}
执行结果
任务结果:null
第一个任务的异常:ava.util.concurrent.CompletionException: java.lang.RuntimeException:
第二个任务的结果
exceptionally()方法
public static void main(String[] args) throws Exception {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
throw new RuntimeException("第一个任务出现异常");
}
return 1;
}).exceptionally(exec -> {
// exceptionally 里面的返回值需要和前面的 stage 返回值保持一致
return Integer.valueOf("2");
});
// 最终的执行结果
System.out.println(completableFuture.get());
}