一、简介
1.1 概述
我们知道Future的有局限性,它没法直接对多个任务进行链式、组合等处理,需要借助并发工具类才能完成,实现逻辑比较复杂。
而CompletableFuture
是对Future的扩展和增强。CompletableFuture
实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture
实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、规则以及方式。
从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch
等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。
CompletableFuture
的继承结构如下:
CompletionStage
接口定义了任务编排的方法,执行某一阶段,可以向下执行后续阶段。异步执行的,默认线程池是ForkJoinPool.commonPool()
,但为了业务之间互不影响,且便于定位问题,强烈推荐使用自定义线程池。
CompletableFuture
中默认线程池如下:
// 根据commonPool的并行度来选择,而并行度的计算是在ForkJoinPool的静态代码段完成的
private static final boolean useCommonPool =
(ForkJoinPool.getCommonPoolParallelism() > 1);
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
ForkJoinPool
中初始化commonPool
的参数
static {
// initialize field offsets for CAS etc
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ForkJoinPool.class;
CTL = U.objectFieldOffset
(k.getDeclaredField("ctl"));
RUNSTATE = U.objectFieldOffset
(k.getDeclaredField("runState"));
STEALCOUNTER = U.objectFieldOffset
(k.getDeclaredField("stealCounter"));
Class<?> tk = Thread.class;
……
} catch (Exception e) {
throw new Error(e);
}
commonMaxSpares = DEFAULT_COMMON_MAX_SPARES;
defaultForkJoinWorkerThreadFactory =
new DefaultForkJoinWorkerThreadFactory();
modifyThreadPermission = new RuntimePermission("modifyThread");
// 调用makeCommonPool方法创建commonPool,其中并行度为逻辑核数-1
common = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction<ForkJoinPool>() {
public ForkJoinPool run() { return makeCommonPool(); }});
int par = common.config & SMASK; // report 1 even if threads disabled
commonParallelism = par > 0 ? par : 1;
}
1.2 功能
1.2.1 常用方法
依赖关系
-
thenApply()
:把前面任务的执行结果,交给后面的Function -
thenCompose()
:用来连接两个有依赖关系的任务,结果由第二个任务返回
and集合关系
-
thenCombine()
:合并任务,有返回值 -
thenAccepetBoth()
:两个任务执行完成后,将结果交给thenAccepetBoth
处理,无返回值 -
runAfterBoth()
:两个任务都执行完成后,执行下一步操作(Runnable类型任务)
or聚合关系
-
applyToEither()
:两个任务哪个执行的快,就使用哪一个结果,有返回值 -
acceptEither()
:两个任务哪个执行的快,就消费哪一个结果,无返回值 -
runAfterEither()
:任意一个任务执行完成,进行下一步操作(Runnable类型任务)
并行执行
-
allOf()
:当所有给定的CompletableFuture
完成时,返回一个新的CompletableFuture
-
anyOf()
:当任何一个给定的CompletablFuture
完成时,返回一个新的CompletableFuture
结果处理
-
whenComplete
:当任务完成时,将使用结果(或 null)和此阶段的异常(或 null如果没有)执行给定操作 -
exceptionally
:返回一个新的CompletableFuture
,当前面的CompletableFuture
完成时,它也完成,当它异常完成时,给定函数的异常触发这个CompletableFuture
的完成
1.2.2 异步操作
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)
这四个方法的区别:
-
runAsync()
以Runnable函数式接口类型为参数,没有返回结果,supplyAsync()
以Supplier函数式接口类型为参数,返回结果类型为U;Supplier接口的get()
是有返回值的(会阻塞) -
使用没有指定Executor的方法时,内部使用
ForkJoinPool.commonPool()
作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。 -
默认情况下
CompletableFuture
会使用公共的ForkJoinPool
线程池,这个线程池默认创建的线程数是 CPU 的核数(也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism
来设置ForkJoinPool线程池的线程数)。如果所有CompletableFuture
共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰