先简单介绍几个常用方法:
阻塞当前CompletableFuture的线程,直到异步调用返回结果,需要手动抛出异常throws InterruptedException, ExecutionException
同get(),不需要手动抛出异常
通过allOf把多个任务连接到一起,调用join()阻塞,直到所有任务都执行完才可继续执行。
接收任务的返回结果作为输入参数,方法内部执行一些操作后,并把任务的返回结果输出。
接收任务的返回结果作为输入参数,方法内部执行一些操作后,不输出结果。
不接受参数,也不输出结果。只在方法内部执行一些操作后
通过allOf方法来实现多个调用的统一返回。
提交10个任务,并获取任务结果进行测试。
//模拟10个任务数据
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<CompletableFuture<Integer>> futures = new ArrayList<>();
System.out.println("程序开始=======");
//提交10个任务
Arrays.stream(arr).collect(Collectors.toList()).stream().forEach(a -> {
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("执行任务ID={}, 当前时间="+ System.currentTimeMillis()/1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return a;
});
futures.add(integerCompletableFuture);
});
//阻塞
System.out.println("CompletableFuture 之前");
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
System.out.println("CompletableFuture 之后");
//打印10个任务结果
long start = System.currentTimeMillis();
futures.forEach(a -> {
System.out.println("任务结果:" + a.join());
});
System.out.println("打印10个任务花费时间:" + (System.currentTimeMillis() - start)/1000);
在这里我们可以将对各future实例添加到allOf方法中,然后通过future的join()获取future的状态。如果allOf里面的所有线程为执行完毕,主线程会阻塞,直到allOf里面的所有线程都执行,线程就会被唤醒。
打印结果:
程序开始=======
CompletableFuture 之前
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979444
执行任务ID={}, 当前时间=1594979444
执行任务ID={}, 当前时间=1594979444
CompletableFuture 之后
任务结果:7
任务结果:2
任务结果:6
任务结果:8
任务结果:10
任务结果:3
任务结果:5
任务结果:4
任务结果:1
任务结果:9
打印10个任务花费时间:0
打印的时间戳转为了秒,可以看到前7条一起执行,过了两秒,又执行了3条。
这是因为我的电脑是4核8线程,而CompletableFuture的线程池是ForkJoinPool.commonPool(),默认使用 CPU核心数-1个线程数,而为什么是8-1=7,而不是4-1=3呢?看下图你就明白了
从CompletableFuture的supplyAsync()方法,分析默认线程池:
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 ForkJoinPool makeCommonPool() {
...
...
if (parallelism < 0 && // default 1 less than #cores
(parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
parallelism = 1;
if (parallelism > MAX_CAP)
parallelism = MAX_CAP;
return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
"ForkJoinPool.commonPool-worker-");
}
异步编程,不想阻塞线程的话,可以使用thenAccpt、thenApply、thenRun,future结束后,执行异步方法
CompletableFuture可以实现如下等功能
消费和运行的区别:
消费使用执行结果。运行则只是运行特定任务。具体其他功能大家可以根据需求自行查看。
CompletableFuture借助CompletionStage的方法可以实现链式调用。并且可以选择同步或者异步两种方式。