目录
一. 前言
CompletableFuture继承于java.util.concurrent.Future,它本身具备Future的所有特性,并且基于JDK1.8的流式编程以及Lambda表达式等实现一元操作符、异步性以及事件驱动编程模型,可以用来实现多线程的串行关系,并行关系,聚合关系。它的灵活性和更强大的功能是Future无法比拟的。
二. 创建方式
2.1. 用默认线程池
CompletableFuture<String> future = new CompletableFuture<>();
默认使用 ForkJoinPool.commonPool(),commonPool是一个会被很多任务 共享 的线程池,比如同一 JVM 上的所有 CompletableFuture、并行 Stream 都将共享 commonPool,commonPool 设计时的目标场景是运行 非阻塞的 CPU 密集型任务,为最大化利用 CPU,其线程数默认为 CPU 数量 -1。
2.2. 用自定义线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy());
CompletableFuture.runAsync(() -> System.out.println("Hello World!"), pool);
三. 构建异步任务
3.1. runAsync
runAsync() 进行数据处理,接收前一步骤传递的数据,无返回值。
// 源码
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
// 示例
public static void runAsync() {
// 使用默认线程池
CompletableFuture cf = CompletableFuture.runAsync(() -> System.out.println("Hello World!"));
assertFalse(cf.isDone());
// 使用自定义线程池
CompletableFuture.runAsync(() -> System.out.println("Hello World!"),
Executors.newSingleThreadExecutor());
}
3.2. supplyAsync
supplyAsync() 进行数据处理,接收前一步骤传递的数据,处理加工后返回。有返回值,返回数据类型可以和前一步骤返回的数据类型不同。
// 源码
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
// 示例
public static void supplyAsync() throws ExecutionException, InterruptedException {
CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> {
try {
//ForkJoinPool.commonPool-worker-1线程
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
});
//阻塞等待3秒
String result = f.get();
//main线程
System.out.println(Thread.currentThread().getName());
System.out.println(result);
}
3.3. anyOf
anyOf(CompletableFuture<?>... cfs) 合并多个complete为一个,等待其中之一完成。当任何一个CompletableFuture完成的时候【相同的结果类型】,返回一个新的CompletableFuture。
// 示例
private static void anyOf() throws ExecutionException, InterruptedException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of Future 1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of Future 2";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of Future 3";
});
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);
System.out.println(anyOfFuture.get()); // Result of Future 2
}
3.4. allOf
allOf(CompletableFuture<?>... cfs) 合并多个complete为一个,等待全部完成。当CompletableFuture全部完成的时候【相同的结果类型】,返回一个新的CompletableFuture。
// 示例
// 创建异步执行任务:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
});
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return 3.2;
});
CompletableFuture<Double> cf3 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job3,time->"+System.currentTimeMillis());
try {
Thread.sleep(1300);
} catch (InterruptedException e) {
}
// throw new RuntimeException("test");
System.out.println(Thread.currentThread()+" exit job3,time->"+System.currentTimeMillis());
return 2.2;
});
//allof等待所有任务执行完成才执行cf4,如果有一个任务异常终止,则cf4.get时会抛出异常,都是正常执行,cf4.get返回null
//anyOf是只有一个任务执行完成,无论是正常执行或者执行异常,都会执行cf4,cf4.get的结果就是已执行完成的任务的执行结果
CompletableFuture cf4=CompletableFuture.allOf(cf,cf2,cf3).whenComplete((a,b)->{
if(b!=null){
System.out.println("error stack trace->");
b.printStackTrace();
}else{
System.out.println("run succ,result->"+a);
}
});
System.out.println("main thread start cf4.get(),time->"+System.currentTimeMillis());
//等待子任务执行完成
System.out.println("cf4 run result->"+cf4.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
四. CompletableFuture 接口
4.1. 基本方法
isDone():判断任务是否完成。三种完成情况:normally(正常执行完毕)、exceptionally(执行异常)、via cancellation(取消)。
get():阻塞获取结果或抛出受检测异常,需要显示进行try...catch处理。
get(long timeout,TimeUnit unit):超时阻塞获取结果。
getNow(T valueIfAbsent):若当前任务无结果,则返回valueIfAbsent,否则返回已完成任务的结果。
getNumberOfDependents():返回依赖当前任务的任务数量,主要用于监控。
cancel(boolean mayInterruptIfRunning):取消任务,若一个任务未完成,则发生CancellationException异常。其相关未完成的子任务也会以CompletionException结束。
isCancelled():是否已取消,在任务正常执行完成前取消,才为true。否则为false。
join():阻塞获取结果或抛出非受检异常。
complete(T value):设置任务结果,任务正常结束,之后的任务状态为已完成。
completeExceptionally(Throwable ex):设置任务异常结果,任务异常结束,之后的任务状态为已完成。
completeOnTimeout(T value,long timeout,TimeUnit unit):JDK9新增,设置任务完成超时时间,若在指定时间内未正常完成,则以给定的value为任务结果。
isCompletedExceptionally():判断任务是否异常结束。异常可能的原因有:取消、显示设置任务异常结果、任务动作执行异常等。
orTimeout(long timeout,TimeUnit unit):JDK9新增,设置任务完成超时时间,若在指定时间内未正常完成,则任务会以异常(TimeoutException)结束。
4.2. 单任务结果消费
4.2.1. thenApply
thenApply():在前一个阶段上应用thenApply函数,将上一阶段完成的结果作为当前阶段的入参,有返回值。
// 源码
public <U> CompletableFuture<U> thenApply(
Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(
Function<? super T,? extends U> fn) {
return uniApplyStage(asyncPool, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(
Function<? super T,? extends U> fn, Executor executor) {
return uniApplyStage(screenExecutor(executor), fn);
}
// 示例
public static void thenApply() throws ExecutionException, InterruptedException {
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
System.out.println(s);
return s.toUpperCase();
}).thenApply(s->{
System.out.println(s);
return s + ":body";
});
System.out.println(cf.get());
}
then意味着这个阶段的动作发生当前的阶段正常完成之后。本例中,当前节点完成,返回字符串message。
apply意味着返回的阶段将会对结果前一阶段的结果应用一个函数。函数的执行会被阻塞。
4.2.2. thenAccept
thenAccept():消费前一阶段的结果,无返回值。
// 源码
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
return uniAcceptStage(asyncPool, action);
}
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,
Executor executor) {
return uniAcceptStage(screenExecutor(executor), action);
}
// 示例
public static void thenAccept() throws InterruptedException {
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
return "message";
}).thenAccept((consumer) -> {
System.out.println(consumer);
});
}
4.2.3. thenRun
thenRun():当上一阶段完成后,执行本阶段的任务,无返回值,并且无入参。
// 源码
public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}
public CompletableFuture<Void> thenRunAsync(Runnable action) {
return uniRunStage(asyncPool, action);
}
public CompletableFuture<Void> thenRunAsync(Runnable action,
Executor executor) {
return uniRunStage(screenExecutor(executor), action);
}
// 示例
public static void thenRun() throws InterruptedException {
CompletableFuture.supplyAsync(() -> {
//执行异步任务
System.out.println("执行任务");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
}).thenRun(() -> {
// Computation Finished.
System.out.println("上一阶段任务执行完成");
});
Thread.sleep(2000);
}
4.3. 合并消费结果
4.3.1. thenCombine
thenCombine():合并另外一个任务,两个任务都完成后,执行BiFunction,入参为两个任务结果,返回新结果。
如果CompletableFuture依赖两个前面阶段的结果, 它复合两个阶段的结果再返回一个结果,我们就可以使用thenCombine()函数。整个流水线是同步的。
// 源码
public <U,V> CompletableFuture<V> thenCombine(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn) {
return biApplyStage(null, other, fn);
}
public <U,V> CompletableFuture<V> thenCombineAsync(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn) {
return biApplyStage(asyncPool, other, fn);
}
public <U,V> CompletableFuture<V> thenCombineAsync(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
return biApplyStage(screenExecutor(executor), other, fn);
}
// 示例
public static void thenCombine() {
CompletableFuture<String> cfA = CompletableFuture.supplyAsync(() -> {
System.out.println("processing a...");
return "hello";
});
CompletableFuture<String> cfB = CompletableFuture.supplyAsync(() -> {
System.out.println("processing b...");
return " world";
});
CompletableFuture<String> cfC = CompletableFuture.supplyAsync(() -> {
System.out.println("processing c...");
return ", I'm CodingTao!";
});
cfA.thenCombine(cfB, (resultA, resultB) -> {
System.out.println(resultA + resultB); // hello world
return resultA + resultB;
}).thenCombine(cfC, (resultAB, resultC) -> {
System.out.println(resultAB + resultC); // hello world, I'm CodingTao!
return resultAB + resultC;
});
}
4.3.2. thenAcceptBoth
thenAcceptBoth():合并另外一个任务,两个任务都完成后,执行这个方法等待第一个阶段的完成,它的结果传给一个指定的返回CompletableFuture函数,它的结果就是返回的CompletableFuture的结果,入参为两个任务结果,不返回新结果。
public <U> CompletableFuture<Void> thenAcceptBoth(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action) {
return biAcceptStage(null, other, action);
}
public <U> CompletableFuture<Void> thenAcceptBothAsync(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action) {
return biAcceptStage(asyncPool, other, action);
}
public <U> CompletableFuture<Void> thenAcceptBothAsync(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action, Executor executor) {
return biAcceptStage(screenExecutor(executor), other, action);
}
// 示例
private static void thenAcceptBoth() {
CompletableFuture<String> cfA = CompletableFuture.supplyAsync(() -> "resultA");
CompletableFuture<String> cfB = CompletableFuture.supplyAsync(() -> "resultB");
cfA.thenAcceptBoth(cfB, (resultA, resultB) -> {
//resultA,resultB
System.out.println(resultA+","+resultB);
});
}
4.3.3. runAfterBoth
runAfterBoth():合并另外一个任务,两个任务都完成后,执行Runnable,无返回值,无入参,注意,这里的两个任务是同时执行。
// 源码
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
Runnable action) {
return biRunStage(null, other, action);
}
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
Runnable action) {
return biRunStage(asyncPool, other, action);
}
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
Runnable action,
Executor executor) {
return biRunStage(screenExecutor(executor), other, action);
}
// 示例
private static void runAfterBoth() {
CompletableFuture<String> cfA = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("process a");
return "resultA";
});
CompletableFuture<String> cfB = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("process b");
return "resultB";
});
cfB.runAfterBoth(cfA, () -> {
//resultA,resultB
System.out.println("任务A和任务B同时完成");
});
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
4.4. 任一结果消费
4.4.1. applyToEither
applyToEither():其中任一任务完成后,执行Function,结果转换,入参为已完成的任务结果。返回新结果,要求两个任务结果为同一类型。
// 源码
public <U> CompletableFuture<U> applyToEither(
CompletionStage<? extends T> other, Function<? super T, U> fn) {
return orApplyStage(null, other, fn);
}
public <U> CompletableFuture<U> applyToEitherAsync(
CompletionStage<? extends T> other, Function<? super T, U> fn) {
return orApplyStage(asyncPool, other, fn);
}
public <U> CompletableFuture<U> applyToEitherAsync(
CompletionStage<? extends T> other, Function<? super T, U> fn,
Executor executor) {
return orApplyStage(screenExecutor(executor), other, fn);
}
// 示例
private static void applyToEither() throws ExecutionException, InterruptedException {
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "通过方式A获取商品a";
});
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "通过方式B获取商品a";
});
CompletableFuture<String> futureC = futureA.applyToEither(futureB, product -> "结果:" + product);
//结果:通过方式A获取商品a
System.out.println(futureC.get());
}
4.4.2. acceptEither
acceptEither():其中任一任务完成后,执行Consumer,消费结果,入参为已完成的任务结果。不返回新结果,要求两个任务结果为同一类型。
// 源码
public CompletableFuture<Void> acceptEither(
CompletionStage<? extends T> other, Consumer<? super T> action) {
return orAcceptStage(null, other, action);
}
public CompletableFuture<Void> acceptEitherAsync(
CompletionStage<? extends T> other, Consumer<? super T> action) {
return orAcceptStage(asyncPool, other, action);
}
public CompletableFuture<Void> acceptEitherAsync(
CompletionStage<? extends T> other, Consumer<? super T> action,
Executor executor) {
return orAcceptStage(screenExecutor(executor), other, action);
}
4.4.3. runAfterEither
runAfterEither():其中任一任务完成后,执行Runnable,消费结果,无入参,不返回新结果,不要求两个任务结果为同一类型。
场景:假设查询商品a,有两种方式,A和B,但是A和B的执行速度不一样,希望哪个先返回就用那个的返回值。
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
Runnable action) {
return orRunStage(null, other, action);
}
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
Runnable action) {
return orRunStage(asyncPool, other, action);
}
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
Runnable action,
Executor executor) {
return orRunStage(screenExecutor(executor), other, action);
}
4.5. 级联任务
4.5.1. thenCompose
thenCompose():当原任务完成后,以其结果为参数,返回一个新的任务(而不是新结果,类似flatMap)。
这个方法等待第一个阶段的完成,它的结果传给一个指定的返回CompletableFuture函数,它的结果就是返回的CompletableFuture的结果。
// 源码
public <U> CompletableFuture<U> thenCompose(
Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(null, fn);
}
public <U> CompletableFuture<U> thenComposeAsync(
Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(asyncPool, fn);
}
public <U> CompletableFuture<U> thenComposeAsync(
Function<? super T, ? extends CompletionStage<U>> fn,
Executor executor) {
return uniComposeStage(screenExecutor(executor), fn);
}
// 示例
private static void thenCompose() {
String original = "Message";
CompletableFuture cf = CompletableFuture.completedFuture(original).thenApply(s -> delayedUpperCase(s))
.thenCompose(upper -> CompletableFuture.completedFuture(original).thenApply(s -> delayedLowerCase(s))
.thenApply(s -> upper + s));
// MESSAGEmessage
System.out.println(cf.join());
}
4.6. 单任务结果或异常消费
CompletableFuture.supplyAsync(() -> "resultA")
.thenApply(resultA -> resultA + " resultB")
.thenApply(resultB -> resultB + " resultC")
.thenApply(resultC -> resultC + " resultD");
上面的代码中,任务 A、B、C、D 依次执行,如果任务 A 抛出异常(当然上面的代码不会抛出异常),那么后面的任务都得不到执行。如果任务 C 抛出异常,那么任务 D 得不到执行。
那么我们怎么处理异常呢?看下面3节的示例代码,我们在任务 A 中抛出异常,并对其进行处理:
4.6.1. handle
handle():任务完成后执行BiFunction,结果转换,入参为结果或者异常,返回新结果。
// 源码
public <U> CompletableFuture<U> handle(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(null, fn);
}
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(asyncPool, fn);
}
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
return uniHandleStage(screenExecutor(executor), fn);
}
// 示例
private static void handle() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "resultA")
.thenApply(resultA -> resultA + " resultB")
// 任务 C 抛出异常
.thenApply(resultB -> {throw new RuntimeException();})
// 处理任务 C 的返回值或异常
.handle(new BiFunction<Object, Throwable, Object>() {
@Override
public Object apply(Object re, Throwable throwable) {
if (throwable != null) {
return "errorResultC";
}
return re;
}
})
.thenApply(resultC -> {
System.out.println("resultC:" + resultC);
return resultC + " resultD";
});
System.out.println(future.join());
}
4.6.2. whenComplete
whenComplete():任务完成后执行BiConsumer,结果消费,入参为结果或者异常,不返回新结果。
// 源码
public CompletableFuture<T> whenComplete(
BiConsumer<? super T, ? super Throwable> action) {
return uniWhenCompleteStage(null, action);
}
public CompletableFuture<T> whenCompleteAsync(
BiConsumer<? super T, ? super Throwable> action) {
return uniWhenCompleteStage(asyncPool, action);
}
public CompletableFuture<T> whenCompleteAsync(
BiConsumer<? super T, ? super Throwable> action, Executor executor) {
return uniWhenCompleteStage(screenExecutor(executor), action);
}
// 示例
private static void whenComplete() throws ExecutionException, InterruptedException {
// 创建异步执行任务:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+"job1 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(true){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+"job1 exit,time->"+System.currentTimeMillis());
return 1.2;
}
});
//cf执行完成后会将执行结果和执行过程中抛出的异常传入回调方法
// 如果是正常执行,a=1.2,b则传入的异常为null
//如果异常执行,a=null,b则传入异常信息
CompletableFuture<Double> cf2=cf.whenComplete((a,b)->{
System.out.println(Thread.currentThread()+"job2 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(b!=null){
System.out.println("error stack trace->");
b.printStackTrace();
}else{
System.out.println("run succ,result->"+a);
}
System.out.println(Thread.currentThread()+"job2 exit,time->"+System.currentTimeMillis());
});
//等待子任务执行完成
System.out.println("main thread start wait,time->"+System.currentTimeMillis());
//如果cf是正常执行的,cf2.get的结果就是cf执行的结果
//如果cf是执行异常,则cf2.get会抛出异常
System.out.println("run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
4.6.3. exceptionally
exceptionally():任务异常,则执行Function,异常转换,入参为原任务的异常信息,若原任务无异常,则返回原任务结果,即不执行转换。
// 源码
public CompletableFuture<T> exceptionally(
Function<Throwable, ? extends T> fn) {
return uniExceptionallyStage(fn);
}
五. 总结
1. 以Async结尾的方法,都是异步方法,对应的没有Async则是同步方法,一般都是一个异步方法对应一个同步方法。
2. 以Async后缀结尾的方法,都有两个重载的方法,一个是使用内容的forkjoin线程池,一种是使用自定义线程池。
3. 以run开头的方法,其入口参数一定是无参的,并且没有返回值,类似于执行Runnable方法。
4. 以supply开头的方法,入口也是没有参数的,但是有返回值。
5. 以Accept开头或者结尾的方法,入口参数是有参数,但是没有返回值。
6. 以Apply开头或者结尾的方法,入口有参数,有返回值。
7. 带有either后缀的方法,表示谁先完成就消费谁。
记住上面几条,基本上就可以记住大部分的方法,剩下的其他方法,就可以单独记忆了。
本文介绍了CompletableFuture的使用,它继承于Future,可实现多线程的串行、并行和聚合关系。文中阐述了其创建方式,包括使用默认和自定义线程池;构建异步任务的方法,如runAsync、supplyAsync等;还详细讲解了CompletableFuture接口的各类方法及功能。
9808

被折叠的 条评论
为什么被折叠?



