背景:
CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。
使用场景:多个任务异步执行,互不影响,提高应用的响应速度和吞吐量。
提供了不用回调的API、以及可以回调的API。
常见API:
runAsync:
执行异步任务,该API没有回调。
//默认使用ForkJoinPool线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
//指定线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
supplyAsync:
执行异步任务,该API有返回值
// 使用默认的ForkJoinPool线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 有返回值 可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
allOf、anyof:
等待所有任务完成。
// 等待所有任务完成
CompletableFuture.allOf(future1, future2, future3).get();
CompletableFuture.allOf(future1, future2, future3).join();
// 只要任意一个任务完成即可
CompletableFuture.anyOf(future1, future2, future3).get();
CompletableFuture.anyOf(future1, future2, future3).join();
获取返回值:
有三个API。
join:返回结果或者抛出一个unchecked异常(CompletionException),不需要显示捕获异常。
get:返回结果或者一个具体的异常(ExecutionException, InterruptedException),此方法继承至Future是堵塞的。
getNow:如果当前任务执行完成,返回执行结果,否则返回valueIfAbsent(默认值)。
/**
* 通过get获取方法
*/
public void test1() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "get方法需要显示捕获异常");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
/**
* join 不需要显示捕获异常
*/
public void test2() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "join方法不需要显示捕获异常");
System.out.println(future.join());
}
/**
* getNow方法可以设置默认值
* 在有效的时间内,未返回结果,则直接返回默认值
*/
public void test3() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "getNow获取返回值");
System.out.println(future.getNow("默认值"));
}
并行实践:
long startMills = System.currentTimeMillis();
System.out.println(startMills);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "1";
}
);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "2";
}
);
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2);
allOfFuture.thenRun(() -> System.out.println("All futures completed"));
allOfFuture.join();
long endMills = System.currentTimeMillis();
System.out.println(endMills);
注意,这里join方法才能堵塞,allOf方法只是一个占位符。如果不调用join方法,这段代码不会有3秒的时间差。只有调用join、get等方法,才能达到future1、future2一起执行完,才接着下一步打印的操作。
踩坑点:
不设置线程池,其默认是利用共有的ForkJoinPool实现的异步。
随便拿个API看源码。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
private static final boolean useCommonPool =
(ForkJoinPool.getCommonPoolParallelism() > 1);
可以看出来,并行程度高的话,会使用ForkJoinPool线程池的池子(commonPool)。