前言
CompletableFuture
是Java8中并发包提供的异步编程实现类,它是Future
接口的扩展接口,它拥有比Future
更强大的能力。
Future接口回顾
Future接口提供下面方法
java
public interface Future<V> {
// 取消任务执行
boolean cancel(boolean mayInterruptIfRunning);
// 判断任务是否取消
boolean isCancelled();
// 任务是否完成
boolean isDone();
// 获取执行结果(阻塞)
V get() throws InterruptedException, ExecutionException;
// 最多等待给定的时间获取任务执行结果,否则抛出超时异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future代码演示
java
// 1. 创建线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
// 2.创建FutureTask任务
FutureTask<String> futureTask = new FutureTask<>(() -> {
TimeUnit.SECONDS.sleep(5);
return "任务执行成功";
});
// 3. 将FutureTask提交到线程池
executorService.submit(futureTask);
System.out.println(LocalTime.now() + "--->开始等待执行结果");
// 4. 获取执行结果
String result = futureTask.get();
System.out.println(LocalTime.now() + "<---" + result);
executorService.shutdown();
执行结果如下所示,主线程调用FutureTask.get()
方法阻塞,等待了5秒才获取到执行结果
Future接口的局限性
Future
获取执行结果是阻塞的
Future
并不支持回调,要想获取任务执行的结果,只能通过阻塞的get()
方法或者轮询判断执行是否成功再获取执行结果
- 多个Future无法合并
例如我们有一种场景,需要先执行10的Future
,等10个Future
任务全部执行完毕后,再运行某个函数,Future
并不支持
Future
接口没有异常处理API
Future
没有异常处理API,如果抛出异常
CompletableFuture
CompletableFuture
是对Future
的增强,它不仅支持原来Future
的能力,并在此基础上进行了扩展,同时CompletableFuture
实现了对任务的编排能力,我们可以使用CompletableFuture
提供的API轻松的的编排不同任务的运行顺序。从下面类图可以看到CompletableFuture
实现了CompletionStage<T>
和Future<T>
接口。
CompletionStage代表异步计算的一个阶段,它可以是另一个CompletionStage完成时执行一个操作或计算一个值。一个CompletionStage可以是由其他阶段来触发,它完成时也可以触发其他CompletionStage。CompletableFuture实现了CompletionStage接口,使其具备了任务编排的能力
构建CompletableFuture
构建CompletableFuture
可以通过下面5个方法
java
// 创建一个异步有返回值的CompletableFuture,使用ForkJoinPool.commonPool()执行
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 创建一个异步有返回值的CompletableFuture,使用自定义线程池执行
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
// 创建一个异步无返回值的CompletableFuture,使用ForkJoinPool.commonPool()执行
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 创建一个异步无返回值的CompletableFuture,使用自定义线程池执行
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
// 创建一个执行结果是value,已经完成的CompletableFuture
public static <U> CompletableFuture<U> completedFuture(U value)
虽然CompletableFuture
的构造方法是public的,但是还是不建议直接使用new关键字创建CompletableFuture
,通过new关键字构建的CompletableFuture
是未完备的CompletableFuture
,我们可以通过get()
方法的源码就可以看出端倪
java
// java.util.concurrent.CompletableFuture#get()
// 调用get()方法时,如果CompletableFuture中result不为空,则会直接返回result
public T get() throws InterruptedException, ExecutionException {
Object r;
return reportGet((r = result) == null ? waitingGet(true) : r);
}
// java.util.concurrent.CompletableFuture#completedFuture
// 通过completedFuture传入的参数会作为CompletableFuture的构造参数
public static <U> CompletableFuture<U> completedFuture(U value) {
return new CompletableFuture<U>((value == null) ? NIL : value);
}
// private构造函数,将入参赋值给result
private CompletableFuture(Object r) {
this.result = r;
}
从上面源码可以分析出通过new关键字构建的
CompletableFuture
的未完备指的是没有直接给result赋值或者没有提供Supplier或者Runner,因此还是建议通过上面5个方法构建CompletableFuture
CompletableFuture实现Future能力演示
java
// 1. 创建线程池
ExecutorService executors = Executors.newSingleThreadExecutor();
// 2. 创建CompletableFuture
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] 开始执行任务");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] 任务执行完毕");
return "success";
}, executors);
// 3. 通过get方法阻塞获取执行结果
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] 开始获取执行结果");
String result = completableFuture.get();
System.out.println(LocalTime.now() + "<---[" + Thread.currentThread().getName() + "] 获取执行结果"+result);
// 4. 关闭线程池
executors.shutdown();
执行结果如下图所示,主线程调用get()
方法后,等待CompletableFuture
中的任务执行完毕后才获取到执行结果,可以看出上面代码执行的过程与Future
中提供的能力相似
CompletableFuture任务编排
上面CompletableFuture
调用get()
方法是阻塞的,需要一直等待任务执行完毕才会返回结果,如果我们想要异步回调,可以使用下面的方法
thenApply()
thenApply总共有下面三个方法,它的作用上一个阶段完成时,接收CompletableFuture
执行的结果作为参数,执行Function
代码,生成一个新返回值的CompletableFuture
thenApply(Function<? super T,? extends U> fn)
CompletableFuture
执行完毕后,接收CompletableFuture
执行的结果作为参数,执行Function
,生成一个新CompletableFuture
,这两个任务都是在同一个线程池中运行
thenApplyAsync(Function<? super T,? extends U> fn)
CompletableFuture
执行完毕后,接收CompletableFuture
执行的结果作为参数,异步执行Function
,生成一个新返回值的CompletableFuture
,默认是ForkJoinPool.commonPool()
thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
CompletableFuture
执行完毕后,接收CompletableFuture
执行的结果作为参数,使用指定线程池异步执行Function
,生成一个新返回值的CompletableFuture
then后面同步和异步是相对于之前的CompletableFuture任务线程池来说的
thenApply()
实战演示代码如下,我们先创建一个任务类Task
供后续使用,它的主要任务是打印输出任务情况
java
public class CommonTask implements Function<String,String>, Consumer<String>,Runnable {
private final long sleepTime;
private final String taskName;
public CommonTask(long sleepTime, String taskName) {
this.sleepTime = sleepTime;
this.taskName = taskName;
}
@Override
public String apply(String result) {
log(result);
return taskName + " success!";
}
@Override
public void accept(String result) {
log(result);
}
@Override
public void run() {
log(null);
}
private void log(String result) {
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+taskName+"执行中,获取到上个任务结果" + result);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+taskName+"执行完毕!");
}
}
实现ThreadFactory
java
public class ThreadFactoryImpl implements ThreadFactory {
private int i = 0;
private final String namePrefix;
public ThreadFactoryImpl(String name) {
this.namePrefix = name;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName(namePrefix+" - "+(++i));
return thread;
}
}
我们先创建一个parent CompletableFuture
,然后利用thenApply
,创建三个子CompletableFuture
,任务依赖关系如下
代码如下
java
// 1. 创建线程池
ExecutorService mainExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("parent"));
ExecutorService executors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("apply executors"));
// 2. 创建CompletableFuture
CompletableFuture<String> cf = CompletableFuture.supplyAsync(new CommonTask(1000,"parent"),mainExecutors);
CompletableFuture<String> cf1 = cf.thenApply(new CommonTask(2500, "thenApply"));
CompletableFuture<String> cf2 = cf.thenApplyAsync(new CommonTask(2000, "thenApplyAsync"));
CompletableFuture<String> cf3 = cf.thenApplyAsync(new CommonTask(1500, "thenApplyAsync executors"), executors);
// 3. 等待cf任务执行
CompletableFuture.allOf(cf1, cf2, cf3).join();
// 4. 关闭线程池
mainExecutors.shutdown();
executors.shutdown();
执行代码,获得如下执行结果,我们可以看出下面三个结论
- 三个子任务
cf1
,cf2
,cf3
开始执行时间与代码中的顺序不一样, thenApply()
使用的是parent
线程池中的线程执行的thenApplyAsync()
如果没有传入指定线程池,默认会使用ForkJoinPool.commonPool
thenAccept()
thenAccept()
与thenApply()
类似,也包含三个方法,不同的是thenAccept()
接收的是Consumer
任务,下个任务执行完毕后返回CompletableFuture<Void>
thenAccept(Consumer<? super T> action)
上个任务完成后,使用上个CcompletableFuture
线程池,执行Consumer
中的任务
thenAcceptAsync(Consumer<? super T> action)
上个任务完成后,异步执行Consumer
中的任务,默认是ForkJoinPool.commonPool()
thenAcceptAsync(Consumer<? super T> action,Executor executor)
上个任务完成后,使用指定线程池异步执行Consumer
中的任务
thenAccept演示代码与thenApply类似,如下所示
java
// 1. 创建线程池
ExecutorService mainExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("parent"));
ExecutorService executors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("accept executors"));
// 2. 创建CompletableFuture
CompletableFuture<String> cf = CompletableFuture.supplyAsync(new CommonTask(1000,"parent"),mainExecutors);
cf.thenAccept(new CommonTask(2500, "thenAccept"));
cf.thenAcceptAsync(new CommonTask(2000, "thenAcceptAsync"));
cf.thenAcceptAsync(new CommonTask(1500, "thenAcceptAsync executors"), executors);
Thread.sleep(3000);
// 3. 关闭线程池
mainExecutors.shutdown();
executors.shutdown();
执行结果如下
thenRun()
thenRun()
也与上面类似,包含三个方法,上个任务执行完毕之后执行Runnable
的任务
thenRun(Runnable action)
上个任务完成后,使用上个CcompletableFuture
线程池,执行Runnable
任务
thenRunAsync(Runnable action)
上个任务完成后,异步执行Runnable
中的任务,默认是ForkJoinPool.commonPool()
thenRunAsync(Runnable action,Executor executor)
上个任务完成后,使用指定线程池异步执行Runnable
中的任务
thenCombine()
thenCombine()
可以合并两个CompletableFuture
的执行结果,等待两个任务完成后,将任务结果传给TaskC
,再继续执行后续任务
thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
同步合并CompletableFuture
执行结果,第一个入参是要合并的CompletionStage
,另一个入参是两个CompletableFuture
结果合并的BiFunction
thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
异步合并CompletableFuture
执行结果,参数与同步的相同
thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)
使用指定的线程池异步合并CompletableFuture
执行结果,这个方法相比上面两个增加了线程池作为第三个参数
thenAcceptBoth()
theAcceptBoth()
可以看做是无返回值版本的thenCombine()
thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
同步消费CompletableFuture
执行结果,第一个入参是要的消费CompletionStage
,另一个入参是两个CompletableFuture
结果消费的BiConsumer
thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
异步消费CompletableFuture
执行结果,参数与同步的相同
thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)
使用指定的线程池异步消费CompletableFuture
执行结果,这个方法相比上面两个增加了线程池作为第三个参数
为了演示thenCombine
的执行效果,我们通过一段比价的代码来理解thenCombine执行结果,任务如下图所示,首先我们通过CompletableFuture
创建两个获取价格任务,然后通过thenCombine
比较两者的价格,获取最低价
代码如下所示
java
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建线程池
ExecutorService jdExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("JD executors"));
ExecutorService taobaoExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("Taobao executors"));
// 2. 创建JD CompletableFuture
CompletableFuture<Integer> jdCf = CompletableFuture.supplyAsync(() -> {
log("开始获取JD XX商品价格");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int price = 100;
log("完成获取JD XX商品价格:"+price);
return price;
}, jdExecutors);
// 3. 创建Taobao CompletableFuture
CompletableFuture<Integer> taobaoCf = CompletableFuture.supplyAsync(() -> {
log("开始获取Taobao XX商品价格");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int price = 150;
log("完成获取Taobao XX商品价格" + price);
return price;
},taobaoExecutors);
// 4. 比较两者价格
CompletableFuture<Integer> finnalCf = jdCf.thenCombine(taobaoCf, (jdPrice, taobaoPrice) -> {
log("开始比价,JD price:" + jdPrice + " taobao Price:" + taobaoPrice);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int minPrice = Math.min(jdPrice, taobaoPrice);
log("完成比价,最低价格:" + minPrice);
return minPrice;
});
// 5. 关闭线程池
jdExecutors.shutdown();
taobaoExecutors.shutdown();
}
private static void log(String msg) {
System.out.println(LocalTime.now() + "--->["+Thread.currentThread().getName()+"] "+msg);
}
执行结果如下所示,在调用thenCombine
方法时并没有传入指定线程池,默认采用JD executors
runAfterBoth()
runAfterBoth()
可以看做是无入参,无返回值版本的thenCombine()
runAfterBoth(CompletionStage<?> other,Runnable action)
等待两个CompletableFuture
执行完毕,同步执行另一个Runnable
任务
runAfterBoth(CompletionStage<?> other,Runnable action)
等待两个CompletableFuture
执行完毕,异步执行另一个Runnable
任务
runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor)
等待两个CompletableFuture
执行完毕,使用指定线程池异步执行另一个Runnable
任务
runAfterBoth()
与thenAcceptBoth()
类似,可以参考上面例子,这里就不再单独演示了
applyToEither()
两个任务执行,哪个任务执行的快,就会使用那个任务的结果,子任务处理之后生成新的结果
applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
同步执行子任务的方法,子任务使用的线程池与调用applyToEither
方法的CF任务使用相同的线程池
这里会有一个问题,同步执行子任务的方法会用哪个线程来执行呢?前面演示的
thenCombine()
采用的是调用thenCombine()
方法CF的线程池
applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
异步执行子任务的方法,采用ForkJoinPool.commonPool()
线程池
applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)
使用指定线程池异步执行子任务
acceptEither()
两个任务执行,哪个任务执行的快,就会消费那个结果,不会有返回值
acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
同步消费子任务的方法
acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
异步消费子任务的方法,采用ForkJoinPool.commonPool()
线程池
acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)
使用指定线程池异步消费子任务的方法
我们依然采用上面比价的例子演示,分别创建jdCf和taobaoCf两个获取价格的CompletableFuture
,然后我们使用acceptEither
方法打印出先获取到的价格
java
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建线程池
ExecutorService jdExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("JD executors"));
ExecutorService taobaoExecutors = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("Taobao executors"));
// 2. 创建JD CompletableFuture
CompletableFuture<Integer> jdCf = CompletableFuture.supplyAsync(() -> {
log("开始获取JD XX商品价格");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int price = 100;
log("完成获取JD XX商品价格:"+price);
return price;
}, jdExecutors);
// 3. 创建Taobao CompletableFuture
CompletableFuture<Integer> taobaoCf = CompletableFuture.supplyAsync(() -> {
log("开始获取Taobao XX商品价格");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int price = 150;
log("完成获取Taobao XX商品价格" + price);
return price;
},taobaoExecutors);
// 4. 打印出最先获取的价格
jdCf.acceptEither(taobaoCf, (fastResult) -> {
log("完成价格获取,更快获取的价格为:" + fastResult);
});
// 5. 关闭线程池
jdExecutors.shutdown();
taobaoExecutors.shutdown();
}
private static void log(String msg) {
System.out.println(LocalTime.now() + "--->["+Thread.currentThread().getName()+"] "+msg);
}
演示结果如下所示
从上面结果可以看到,执行子任务的线程池是先执行完任务的JD executors
,那么我们调整上面任务的时间,将获取taobao价格的时间设置为1000ms,这样会先执行完taobaoCf
,执行结果如下所示,执行子任务的线程池又换成了Taobao executors
从上面的结果我们可以得到结论,同步执行的
acceptEither()
会采用先执行完任务CompletableFuture
的线程池执行子任务
runAfterEither()
两个任务执行,只要任意一个任务执行完毕,就会开始执行下一个任务(Runnable)
runAfterEither(CompletionStage<?> other,Runnable action)
只要一个任务执行完毕,则同步执行子任务
runAfterEitherAsync(CompletionStage<?> other,Runnable action)
采用ForkJoinPool.commonPool()
线程池执行子任务
runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)
使用指定线程池执行子任务
whenComplete()
whenComplete()
是当任务执行完毕之后的回调方法,会将执行结果或者任务运行期间的异常传递给回调方法,如果正常执行,则异常为空,whenComplete()
与上面类似,有1个同步方法,2个异步方法
whenComplete(BiConsumer<? super T, ? super Throwable> action)
CompletableFuture
任务完成后,同步执行whenComplete()
回调
whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
采用ForkJoinPool.commonPool()
线程池,异步执行回调
whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
指定线程池异步执行回调
handle()
handle()
与whenComplete()
方法类似,也是当任务任务完成时执行回调方法,区别是whenComplete()
没有返回值,handle()
有返回值
handle(BiFunction<? super T, Throwable, ? extends U> fn)
同步handle()
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
采用ForkJoinPool.commonPool()
线程池,回调handle()
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor)
指定线程池异步回调handle()
exceptionally()
CompletableFuture
任务完成时如果发生异常,则后续任务不会执行,CompletableFuture
会将异常传给exceptionally()
处理
exceptionally(Function<Throwable, ? extends T> fn)
allOf()
返回一个新的CompletableFuture
,当所有给定的ComplettableFuture
完成时,该新的ComplextableFuture
已完成。如果任何给定的CompletableFutures
异常完成,那么返回的ComplettableFuture
也会这样做,CompletionException
将此异常作为其原因。
java
CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
allOf()
使用场景是多个任务全部执行完毕后,在执行后续任务,例如我们有三个任务taskA
,taskB
,taskC
,要在这三个任务执行完之后再执行finalTask
任务
代码如下所示
java
// 1.创建三个taskA,taskB,taskC completableFuture
CompletableFuture<String> taskACf = CompletableFuture.supplyAsync(new CommonTask(1000, "taskA"));
CompletableFuture<String> taskBCf = CompletableFuture.supplyAsync(new CommonTask(2000, "taskB"));
CompletableFuture<String> taskCCf = CompletableFuture.supplyAsync(new CommonTask(3000, "taskC"));
// 2. 使用allOf
CompletableFuture<Void> allOfCf = CompletableFuture.allOf(taskACf, taskBCf, taskCCf);
// 3. taskA,taskB,taskC执行完毕之后执行finalTask
CompletableFuture<Void> finalCf = allOfCf.thenRun(new CommonTask(2000, "finalTask"));
finalCf.get();
执行结果如下所示,可以看到taskA
,tackB
,taskC
执行耗时不同,使用allOf()
合并后,finalTask
会等待taskA
,taskB
,taskC
执行完毕后再执行
假设taskA
,tackB
,taskC
中只要有一个任务执行失败,这三个任务相互不会影响,而finalTask
则不会执行,还是上面例子,假设taskB
执行过程中抛出异常,我们可以观察finalTask
执行情况,代码如下所示
java
// 1.创建三个不同的CF任务
CompletableFuture<String> taskACf = CompletableFuture.supplyAsync(new CommonTask(1000, "taskA"));
CompletableFuture<String> taskBCf = CompletableFuture.supplyAsync(()->{
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] taskB执行中");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
throw new RuntimeException("任务B执行失败");
});
CompletableFuture<String> taskCCf = CompletableFuture.supplyAsync(new CommonTask(3000, "taskC"));
// 2. 使用allOf
CompletableFuture<Void> allOfCf = CompletableFuture.allOf(taskACf, taskBCf, taskCCf);
// 3. taskA,taskB,taskC执行完毕之后执行finalTask
CompletableFuture<Void> finalCf = allOfCf.thenRun(new CommonTask(2000, "finalTask"))
.exceptionally((e)->{
System.out.println("final捕获到异常:"+e);
return null;
});
finalCf.get();
执行结果如下,我们可以看到finalTask
并没有执行,exceptionally()
捕获到taskB
抛出的异常
其实还有更极端的场景是假设多个任务都抛出异常了,调用
allOf()
的CompletableFuture
会捕获哪个任务抛出的异常呢?大家可以实践一下
anyOf()
了解了allOf()
方法,我们再来学习anyOf()
就会简单很多,anyOf()
就是上游任务只要有一个任务执行完了,就会执行后续任务,还是拿上面的例子taskA
,tackB
,taskC
只要有一个任务执行完毕之后就执行finalTask
anyOf()
与allOf()
不同的是anyOf()
方法获取的CompletableFuture<Object>
会返回最快执行完成任务的结果
java
// 1.创建三个不同的CF任务
CompletableFuture<String> taskACf = CompletableFuture.supplyAsync(new CommonTask(1000, "taskA"));
CompletableFuture<String> taskBCf = CompletableFuture.supplyAsync(new CommonTask(2000, "taskB"));
CompletableFuture<String> taskCCf = CompletableFuture.supplyAsync(new CommonTask(3000, "taskC"));
// 2. 使用anyOf
CompletableFuture<Object> anyOfCf = CompletableFuture.anyOf(taskACf, taskBCf, taskCCf);
// 3. taskA,taskB,taskC只要一个任务执行完毕之后,就执行anyOf
CompletableFuture<Void> finalCf = anyOfCf.thenAccept((r)->{
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+"finalTask执行中,获取到上个任务结果" + r);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(LocalTime.now() + "--->[" + Thread.currentThread().getName() + "] "+"finalTask执行完毕!");
});
finalCf.get();
执行结果如下,taskA
最快执行完毕,taskA
执行完毕之后,finalTask
立即开始执行了
anyOf()
与allOf()
处理异常方式对比
anyOf()
处理异常的方式与allOf()
并不相同,在还没有任务完成前其中一个发生异常,anyOf()
中的任务并不会相互影响,后续任务不会执行,异常将会抛给exceptionally()
处理。如果已经有任务完成了,后续任务如果发生异常,不会对后续任务有影响,后续任务还可以正常执行。