1、 runAsync 和 supplyAsync方法
CompletableFuture 提供了四个静态方法来创建一个异步操作。
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。runAsync 和 supplyAsync 区别:
- runAsync方法不支持返回值。
- supplyAsync可以支持返回值。
示例
//无返回值
public static void testRunAsync() throws Exception {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
System.out.println("run end ...");
});
future.get();
}
//有返回值
public static void testSupplyAsync() throws Exception {
CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
System.out.println("run end ...");
return System.currentTimeMillis();
});
long time = future.get();
System.out.println("time = "+time);
}
2、计算结果完成时的回调方法
当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
可以看到Action的类型是BiConsumer<? super T,? super Throwable>它可以处理正常的计算结果,或者异常情况。
whenComplete 和 whenCompleteAsync 有什么区别呢?
- Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method. 传递给 non-async 方法作为参数的函数(action)可能会在完成当前的 CompletableFuture 的线程中执行,也可能会在完成方法的调用者线程中执行。
- All async methods without an explicit Executor argument are performed using the
ForkJoinPool.commonPool()(unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interfaceCompletableFuture.AsynchronousCompletionTask. 所有没有Executor 参数的 async 方法都在ForkJoinPool.commonPool()线程池中执行(除非不支持最小并发度为2,这种情况下,每个任务会创建一个新线程去执行)。为了简化监控、调试和追踪,所有生成的异步任务都是接口CompletableFuture.AsynchronousCompletionTask的实例。
上面这段话引用自 CompletableFuture api 文档。这段说明描述了 async 和 non-async 方法的区别,这个说明适用于本文后面提到的所有 async 和 non-async 方法。
对于 non-async 方法,需要特别说明一下。在依赖任务完成之前注册的函数(通过whenComplete、thenApply 等方法),也就是在CompletableFuture 的complete() 被调用之前被注册的函数,将会在完成依赖任务的线程中执行。如果注册的时候,依赖任务已经完成,那么注册的函数将会在调用注册的线程中执行。
示例
public static void testWhenComplete() throws Exception {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
if(new Random().nextInt()%2>=0) {
int i = 12/2;
}
System.out.println(Thread.currentThread().getName()+", run end ...");
});
future.whenComplete(new BiConsumer<Void, Throwable>() {
@Override
public void accept(Void t, Throwable action) {
System.out.println(Thread.currentThread().getName()+", whenComplete 1 执行完成!");
}
});
future.whenComplete(new BiConsumer<Void, Throwable>() {
@Override
public void accept(Void t, Throwable action) {
System.out.println(Thread.currentThread().getName()+", whenComplete 2 执行完成!");
}
});
future.exceptionally(new Function<Throwable, Void>() {
@Override
public Void apply(Throwable t) {
System.out.println(Thread.currentThread().getName()+", 执行失败!"+t.getMessage());
return null;
}
});
TimeUnit.SECONDS.sleep(2);
}
-------------------whenComplete 打印输出
ForkJoinPool.commonPool-worker-1, run end ...
ForkJoinPool.commonPool-worker-1, whenComplete 2 执行完成!
ForkJoinPool.commonPool-worker-1, whenComplete 1 执行完成!
-------------------将测试方法中的whenComplete 换成 whenCompleteAsync 打印输出
ForkJoinPool.commonPool-worker-1, run end ...
ForkJoinPool.commonPool-worker-1, whenCompleteAsync 2 执行完成!
ForkJoinPool.commonPool-worker-2, whenCompleteAsync 1 执行完成!
可以看到,对于whenComplete,两次whenComplete 调用都和上一个任务在同一个线程中执行;而对于whenCompleteAsync,两次whenCompleteAsync 在不同的线程中执行。两种方式都时在ForkJoinPool 线程池中执行,而不是调用线程中执行。
3、 thenApply 方法
第一个任务执行完成后,thenApply 执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,对第一个任务结果做转换处理,并返回转换结果。
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
Function<? super T,? extends U>
T:上一个任务返回结果的类型
U:当前任务的返回值类型
示例
private static void testThenApply() throws Exception {
CompletableFuture<Long> future = CompletableFuture.supplyAsync(new Supplier<Long>() {
@Override
public Long get() {
long result = new Random().nextInt(100);
System.out.println(Thread.currentThread().getName()+", result1="+result);
return result;
}
}).thenApplyAsync(new Function<Long, Long>() {
@Override
public Long apply(Long t) {
long result = t*5;
System.out.println(Thread.currentThread().getName()+", result2="+result);
return result;
}
});
long result = future.get();
System.out.println(Thread.currentThread().getName()+", result=" + result);
}
第二个任务依赖第一个任务的结果。
-------------------thenApply 打印输出
ForkJoinPool.commonPool-worker-1, result1=95
main, result2=475
main, result=475
-------------------thenApplyAsync 打印输出
ForkJoinPool.commonPool-worker-1, result1=13
ForkJoinPool.commonPool-worker-1, result2=65
main, result=65
4、 handle 方法
handle 是执行任务完成时对结果的处理。
handle 方法和 thenApply 方法处理方式基本一样,也是在调用方线程中执行。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);
示例
void testHandle() throws Exception{
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println(Thread.currentThread().getName() + ", in async task" );
int i= 10/0;
return new Random().nextInt(10);
}
}).handle(new BiFunction<Integer, Throwable, Integer>() {
@Override
public Integer apply(Integer param, Throwable throwable) {
int result = -1;
if(throwable==null){
result = param * 2;
}else{
System.out.println(Thread.currentThread().getName() + "," + throwable.getMessage());
}
return result;
}
});
System.out.println(Thread.currentThread().getName() + "," + future.get());
}
测试程序打印输出:
ForkJoinPool.commonPool-worker-1, in async task
main,java.lang.ArithmeticException: / by zero
main,-1
从示例中可以看出,在 handle 中可以根据任务是否有异常来进行做相应的后续处理操作。而 thenApply 方法,如果上个任务出现错误,则不会执行 thenApply 方法。
5、 thenAccept 消费处理结果
接收任务的处理结果,并消费处理,无返回结果。
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
示例
public void testThenAccept() throws Exception{
CompletableFuture<Void> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println(Thread.currentThread().getName() + ", in async task" );
return new Random().nextInt(10);
}
}).thenAccept(integer -> {
System.out.println(Thread.currentThread().getName() + ", integer=" + integer);
});
future.get();
System.out.println(Thread.currentThread().getName() + ", caller" );
}
测试程序打印输出:
ForkJoinPool.commonPool-worker-1, in async task
main, integer=1
main, caller
从示例代码中可以看出,该方法只是消费执行完成的任务,并可以根据上面的任务返回的结果进行处理。
6、thenRun 方法
跟 thenAccept 方法不一样的是,不关心任务的处理结果。只要上面的任务执行完成,就开始执行 thenAccept 。
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
示例
void testThenRun() throws Exception{
CompletableFuture<Void> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
System.out.println(Thread.currentThread().getName() + ", in async task" );
return new Random().nextInt(10);
}
}).thenRun(() -> {
System.out.println(Thread.currentThread().getName() + ", in thenRun" );
});
future.get();
}
-------------------thenApplyAsync 打印输出
ForkJoinPool.commonPool-worker-1, in async task
main, in thenRun
-------------------thenRunAsync 打印输出
ForkJoinPool.commonPool-worker-1, in async task
ForkJoinPool.commonPool-worker-1, in thenRun
7、thenCombine 合并任务
thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
示例
void thenCombine() throws Exception {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println(Thread.currentThread().getName() + ", in async task 1" );
return "hello";
}
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
System.out.println(Thread.currentThread().getName() + ", in async task 2" );
return "hello";
}
});
CompletableFuture<String> result = future1.thenCombine(future2, new BiFunction<String, String, String>() {
@Override
public String apply(String t, String u) {
System.out.println(Thread.currentThread().getName() + ", in combine task" );
return t+" "+u;
}
});
System.out.println(Thread.currentThread().getName() + "," + result.get());
}
-------------------thenCombine 打印输出
ForkJoinPool.commonPool-worker-1, in async task 1
ForkJoinPool.commonPool-worker-1, in async task 2
main, in combine task
main,hello hello
-------------------thenCombineAsync 打印输出
ForkJoinPool.commonPool-worker-1, in async task 1
ForkJoinPool.commonPool-worker-1, in async task 2
ForkJoinPool.commonPool-worker-1, in combine task
main,hello hello
8、thenCombine / thenAcceptBoth / runAfterBoth
thenCombine / thenAcceptBoth / runAfterBoth都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个任务。
区别在于:
- thenCombine:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
- thenAcceptBoth: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
- runAfterBoth 不会把执行结果当做方法入参,且没有返回值。
9、applyToEither / acceptEither / runAfterEither
applyToEither / acceptEither / runAfterEither 都表示:将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个任务。
区别在于:
- applyToEither:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
- acceptEither: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
- runAfterEither: 不会把执行结果当做方法入参,且没有返回值。