CompletableFuture
Java5引入了Future和 FutureTask,用于异步处理。Future可以通过get()方法获取异步的返回值。
在Java8引入了CompletableFuture,CompletableFuture不仅实现了Future接口, 还实现了CompletionStage接口。
CompletableFuture实现了CompletionStage接口,重写thenApply()、thenCombine()等方法。
CompletableFuture类能够处理多个异步任务,还能处理异步回调。还能完成以下操作:
- 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果。
- 等待 Future 集合中的所有任务都完成。
- 仅等待 Future集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同一个值),并返回它的结果。
- 通过编程方式完成一个Future任务的执行(即以手工设定异步操作结果的方式)。
- 应对 Future 的完成事件(即当 Future 的完成事件发生时会收到通知,并能使用 Future 计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)
构建CompletableFuture
构建CompletableFuture主要有两种方式,runAsync和supplyAsync。
- runAsync
runAsync异步处理任务,使用Runnable,构建CompletableFuture。
runAsync方法的参数有两种形式,一种是使用默认的ForkJoinPool,另一种是使用自定义的线程池。
第一种:
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
注意,默认的ForkJoinPool的线程是守护线程,当主线程结束时,ForkJoinPool的线程也会随之结束,会影响异步任务的执行。
因此,建议使用自定义的线程池。
第二种:
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
此处的Executor executor就是指线程池。
示例如下:
public static void runAsyncTest2() {
//该线程池仅用于示例,实际建议使用自定义的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println("completableFuture runAsync2.");
}, executorService);
// executorService.shutdown();
}
- supplyAsync
supplyAsync同样可以构建CompletableFuture。
supplyAsync跟runAsync的区别,主要是supplyAsync有返回值。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
此处的参数supplier其实是一个匿名对象,实现了Supplier
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier是函数式接口,关于函数式接口的理解,详情见参考资料: java8 函数式接口
示例如下:
/**
* supplyAsync使用线程池,构建CompletableFuture
*
*/
public static void supplyAsyncTest2() {
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("supplyAsyncTest2");
return "completableFuture结果";
}, executorService);
// executorService.shutdown();
}
获取CompletableFuture任务的结果
T get() throws InterruptedException, ExecutionException: 获取返回值,会阻塞,还需要处理受检的异常
V get(long timeout,Timeout unit):可以设置阻塞时间,unit为时间的单位(秒/分/时之类)
T getNow(T defaultValue):表示当有了返回结果时会返回结果,如果异步线程抛了异常会返回设置的默认值.
T join():获取返回值,会阻塞
CompletableFuture任务执行中
thenApply(): 接收一个任务的前一阶段的输出作为本阶段的输入,该方法有一个参数,也有返回值。
通过thenApply(),可以在supplyAsync()异步完成后,马上就使用supplyAsync()的返回值,不会阻塞。
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
thenAccept(): 接收一个任务的前一阶段的输出作为本阶段的输入,该方法返回值类型为Void,相当于没有返回值。
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
thenRun(): 根本不关心一个任务的前一阶段的输出,它只负责运行新的Runnable任务,该方法返回值类型为Void,相当于没有返回值。
public CompletableFuture<Void> thenRun(Runnable action)
示例如下:
public static void thenApplyTest() {
CompletableFuture.supplyAsync(() -> 1)
.thenApply(i -> i + 1).thenApply(i -> {
System.out.println("thenApplyTest结果为:"+i*i);
return i * i;
});
}
CompletableFuture任务完成后
whenComplete():接收一个任务的前一阶段的输出作为本阶段的输入,该方法有返回值。还有两个参数,第一个参数是任务的返回值,第二个参数是异常。
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
exceptionally():当运行出现异常时,调用该方法可进行一些补偿操作,设置默认值.如果没有异常,则不会触发。
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
示例如下:
public static void exceptionallyTest() {
// num作为分母,如果改成1,.exceptionally()方法就不会执行
Integer num = 0;
CompletableFuture<Integer> exceptionCf = CompletableFuture.supplyAsync(() -> 1).thenApply(i -> i / num)
.whenComplete((i, e) -> {
System.out.println(i);
})
.exceptionally(e -> {
System.out.println("exceptionallyTest error. "+ e);
System.out.println("异常处理");
return 0;
});
Integer result = exceptionCf.join();
System.out.println("exceptionCf结果为:"+result);
}
多个CompletableFuture任务的组合
thenCombine(): 会将两个任务(CompletableFuture)的执行结果作为方法入参传递到指定方法中,且该方法有返回值;
public <U,V> CompletableFuture<V> thenCombine(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
thenAcceptBoth(): 同样将两个任务(CompletableFuture)的执行结果作为方法入参,但是是个Void的返回值,相当于没有返回值;
public <U> CompletableFuture<Void> thenAcceptBoth(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
runAfterBoth(): 在两个任务(CompletableFuture)之后执行,但没有入参,而且是个Void的返回值,相当于没有返回值。
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
示例如下:
public static void thenCombineTest() {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(()-> {
System.out.println("任务1结束.");
return "Hello";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(()-> {
System.out.println("任务2结束.");
return "World";
});
cf1.thenCombine(cf2, (result1, result2) -> result1 + result2)
.whenComplete((r,e)-> System.out.println("任务1的返回值加上任务2的返回值,结果为:"+r));
}
CompletableFuture任务执行后
allOf就是所有任务都完成时触发。但是是个Void的返回值,相当于没有返回值。
//...表示不定参数,可以有多个CompletableFuture参数
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
anyOf是当入参的completableFuture组中有一个任务执行完毕就返回。返回结果是第一个完成的任务的结果。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
示例如下:
public static void allOfTest() {
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1结束.");
return "futureOneResult";
}, executorService);
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2结束.");
return "futureTwoResult";
},executorService);
CompletableFuture.allOf(cf1, cf2).thenRun(()->{
System.out.println("任务1和任务2都完成了");
});
// CompletableFuture completableFuture = CompletableFuture.anyOf(futureOne, futureTwo);
// //返回结果是第一个完成的任务的结果
// System.out.println(completableFuture.get());
}
参考资料
《java8实战》
https://www.jianshu.com/p/547d2d7761db
https://www.cnblogs.com/fingerboy/p/9948736.html
https://www.jianshu.com/p/6bac52527ca4