老早就想学一下CompletableFuture,但是一直耽搁了,趁最近有时间,先把用法整理下来,方便以后查阅。
CompletableFuture 实现了Future,CompletionStage接口,CompletableFuture提供了4个静态方法来创建一个异步操作。
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
...
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 <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){
...}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor){
...}
}
runAsync方法不支持返回值。supplyAsync可以支持返回值。
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。
/**
* Default executor -- ForkJoinPool.commonPool() unless it cannot
* support parallelism.
*/
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
//无返回值
public static void main(String[] args) {
CompletableFuture completableFuture = runAsync();
System.out.println("现在来等待任务结束:" + new Date());
try {
completableFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("任务结束");
}
public static CompletableFuture runAsync() {
return CompletableFuture.runAsync(() -> {
System.out.println(new Date());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡了3秒,现在是:" + new Date());
});
}
控制台信息
Mon Feb 17 17:11:18 CST 2020
现在来等待任务结束:Mon Feb 17 17:11:18 CST 2020
睡了3秒,现在是:Mon Feb 17 17:11:21 CST 2020
任务结束
//有返回值
public static void main(String[] args) {
CompletableFuture completableFuture = supplyAsync();
System.out.println("现在来等待任务结束:" + new Date());
try {
System.out.println("线程睡了:" + completableFuture.get() + "毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("任务结束");
}
public static CompletableFuture<Integer> supplyAsync() {
return CompletableFuture.supplyAsync(() -> {
System.out.println(new Date());
int i = 0;
try {
i = new Random().nextInt(1000) * 5;
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡了" + i + "毫秒,现在是:" + new Date());
return i;
});
}
Mon Feb 17 17:18:40 CST 2020
现在来等待任务结束:Mon Feb 17 17:18:40 CST 2020
睡了4905毫秒,现在是:Mon Feb 17 17:18:45 CST 2020
线程睡了:4905毫秒
任务结束
当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。
whenComplete无论有没有异常都执行
//whenComplete:执行当前任务的线程执行继续执行 whenComplete 的任务。
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
// whenCompleteAsync:执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
// 如果过程中抛出异常,可以用这个来进行处理
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
通过上面的Action的类型 BiConsumer super T,? super Throwable>
可知它可以处理正常的计算结果,或者异常情况。
功能:当CompletableFuture的计算结果完成,或者抛出异常的时候,都可以进入whenComplete方法执行
CompletableFuture<String> futureA = CompletableFuture.
supplyAsync(() -> "执行结果:" + (100 / 0)) //抛出异常
.thenApply(s -> "apply result:" + s) //未执行
.whenComplete((s, e) -> {
if (s != null) {
System.out.println(s);//未执行
}
if (e == null) {
System.out.println(s);//未执行
} else {
System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
}
})
.exceptionally(e -> {
System.out.println("ex"+e.getMessage()); //ex:java.lang.ArithmeticException: / by zero
return "futureA result: 100"; });
System.out.println(futureA.join());//futureA result: 100
根据控制台,我们可以看出执行流程是这样,supplyAsync->whenComplete->exceptionally,可以看出并没有进入thenApply执行,原因也显而易见,在supplyAsync中出现了异常,thenApply只有当正常返回时才会去执行.而whenComplete不管是否正常执行,还要注意一点,whenComplete是没有返回值的.
我们再来看一个例子,如果我们先调用exceptionally,再调用whenComplete
CompletableFuture<String> futureA = CompletableFuture.
supplyAsync(() -> "执行结果:" + (100 / 0))
.thenApply(s -> "apply result:" + s)
.exceptionally(e -> {
System.out.println("ex:"+e.getMessage()); //ex:java.lang.ArithmeticException: / by zero
return "futureA result: 100";
})
.whenComplete((s, e) -> {
if (e == null) {
System.out.println(s);//futureA result: 100
} else {
System.out.println(e.getMessage());//未执行
}
})
;
System.out.println(futureA.join());//futureA result: 100
代码先执行了exceptionally后执行whenComplete,可以发现,由于在exceptionally中对异常进行了处理,并返回了默认值,whenComplete中接收到的结果是一个正常的结果,被exceptionally美化过的结果,这一点需要留意一下.
thenApply:当前任务正常完成以后执行,当前任务的执行的结果会作为下一任务的输入参数,有返回值.
适用场景 : 多个任务串联执行,下一个任务的执行依赖上一个任务的结果,每个任务都有输入和输出
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
Function super T,? extends U>
T:上一个任务返回结果的类型,U:当前任务的返回值类型
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture<String> futureB = futureA.thenApply(s->s + " world");
CompletableFuture<String> future3 = futureB.thenApply(String::toUpperCase);
System.out.println(future3.get());
后一个任务依赖前一个任务的结果。
上面的代码,我们当然可以先调用future.join()先得到任务A的返回值,然后再拿返回值做入参去执行任务B,而thenApply的存在就在于帮我简化了这一步,我们不必因为等待一个计算完成而一直阻塞着调用线程,而是告诉CompletableFuture你啥时候执行完就啥时候进行下一步. 就把多个任务串联起来了.
handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
//T 传入参数类型,Throwable 异常, U 输出类型
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);
当CompletableFuture的计算结果完成,或者抛出异常的时候,可以通过handle方法对结果进行处理
CompletableFuture<String> futureA = CompletableFuture.
supplyAsync(() -> "执行结果:" + (100 / 0)) //抛出异常
.thenApply(s -> "apply result:" + s) //不运行
.exceptionally(e -> {
System.out.println("ex:" + e.getMessage()); //java.lang.ArithmeticException: / by zero
return "futureA result: 100"; //美化异常
})
.handle((s, e) -> {
// e接收不到异常
if (e == null) {
System.out.println(s);//futureA result: 100
} else {
System.out.println(e.getMessage());//未执行
}
return "handle result:" + (s == null ? "500" : s);
});
System.out.println(futureA.join());//handle result:futureA result: 100
通过控制台,我们可以看出,最后打印的是handle result:futureA result: 100,执行exceptionally后对异常进行了"美化",返回了默认值,那么handle得到的就是一个正常的返回,
我们再试下,先调用handle再调用exceptionally的情况.
CompletableFuture<String> futureA = CompletableFuture.
supplyAsync(() -> "执行结果:" + (100 / 0))
.thenApply(s -> "apply result:" + s) // 未执行
.handle((s, e) -> {
// s == null
if (e == null) {
// e != null
System.out.println(s);//未执行
} else {
System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
}
return "handle result:" + (s == null ? "500" : s);
})
.exceptionally(e -> {
System.out.println("ex:" + e.getMessage()); //未执行
return "futureA result: 100";
});
System.out.println(futureA.join());//handle result:500
根据控制台输出,可以看到先执行handle,打印了异常信息,并对接过设置了默认值500,exceptionally并没有执行,因为它得到的是handle返回给它的值
接收任务的处理结果,并消费处理,无返回结果。如果上个任务出现错误,则不会执行 。
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
当前任务正常完成以后执行,当前任务的执行结果可以作为下一任务的输入参数,无返回值.
CompletableFuture<Void> future = CompletableFuture.supplyAsync(()-> new Random().nextInt(10);)
.thenAccept(integer -> System.out.println(integer););//上一个任务的返回结果可以传到下面这个来
future.get();
}
该方法只是消费执行完成的任务,并可以根据上面的任务返回的结果进行处理。并没有后续的输错操作。
thenRun不关心任务的处理结果。没有输入,也没有返回值,只要上面的任务执行完成,就开始执行 thenAccept 。
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
public static void thenRun() throws Exception{
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> new Random().nextInt(10);)
.thenRun(() -> {
System.out.println("thenRun ...");
});
future.get();
}
该方法同thenAccept
方法类似。不同的是上个任务处理完成后,并不会把计算的结果传给thenRun
方法。只是处理玩任务后,执行thenRun
的后续操作。
thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
BiFunction super T,? super U,? extends V> fn
T:第一个任务的返回值
U:第二个任务的返回值
V:处理后的返回值
//第一个任务
CompletableFuture<Double> futurePrice = CompletableFuture.supplyAsync(() -> 100d);
//第二个任务
CompletableFuture<Double> futureDiscount = CompletableFuture.supplyAsync(() -> 0.8);
CompletableFuture<Double> futureResult = futurePrice.thenCombine(futureDiscount, (price, discount) -> price * discount);
System.out.println("最终价格为:" + futureResult.join()); //最终价格为:80.0
当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth来进行消耗,无返回值
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor);
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() ->{
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1="+t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2="+t);
return t;
}
});
f1.thenAcceptBoth(f2, (t,u)-> {
System.out.println("f1="+t+";f2="+u+";");
});
}
两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。有返回值
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);
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);
System.out.println(futureC.join()); //结果:通过方式A获取商品a
两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作,无返回值.
acceptEither和applyToEither差不多,只是有没有返回值的区别而已
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action,Executor executor);
两个CompletionStage,任何一个完成了都会执行下一步的操作(Runnable)
两个任务都没有返回值,最后的处理结果也没有返回值
public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);
f1.runAfterEither(f2, new Runnable() {
@Override
public void run() {
System.out.println("上面有一个已经完成了。");
}
});
两个CompletionStage,都完成了计算才会执行下一步的操作(Runnable)
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);
f1.runAfterBoth(f2, ()->System.out.println("上面两个任务都执行完成了。"););
thenCompose
方法允许你对两个 CompletionStage
进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
功能:这个方法接收的输入是当前的CompletableFuture的计算值,返回结果将是一个新的CompletableFuture
这个方法和thenApply非常像,**都是接受上一个任务的结果作为入参,执行自己的操作,然后返回.**区别如下:
thenApply()
:它的功能相当于将CompletableFuture
转换成CompletableFuture
,改变的是同一个CompletableFuture中的泛型类型thenCompose()
:用来连接两个CompletableFuture,返回值是一个新的CompletableFuturepublic <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) ;
private static void thenCompose() throws Exception {
//第一个任务
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
System.out.println("t1="+t);
return t;
}
})
//第二个任务
.thenCompose(new Function<Integer, CompletionStage<Integer>>() {
//传入第一个任务的返回值
@Override
public CompletionStage<Integer> apply(Integer param) {
//返回一个新的CompletableFuture
return CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = param *2;
System.out.println("t2="+t);
return t;
}
});
}
});
System.out.println("thenCompose result : "+f.get());
}
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> "hello");
//返回新的CompletableFuture
CompletableFuture<String> futureB = futureA.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "world"));
CompletableFuture<String> future3 = futureB.thenCompose(s -> CompletableFuture.supplyAsync(s::toUpperCase));
System.out.println(future3.join());
// 阻塞到获取结果
public T get()
// 超过给定时间抛出TimeOut异常
public T get(long timeout, TimeUnit unit)
// 如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值
public T getNow(T valueIfAbsent)
// 和get差不多
public T join()
join()和get()的区别:抛出异常的区别
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i = 1/0;
return 100;
});
future.join();
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
....
Caused by: java.lang.ArithmeticException: / by zero
at LearnCompletableFuture.lambda$main$0(LearnCompletableFuture.java:31)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
... 5 more
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i = 1/0;
return 100;
});
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at LearnCompletableFuture.main(LearnCompletableFuture.java:35)
....
Caused by: java.lang.ArithmeticException: / by zero
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
//.get() 执行完全部才继续往下执行
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
//.get() 执行完任意一个 返回其执行完那一个结果
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
anyOf
和applyToEither
区别:
anyOf
:接受任意多的CompletableFuture
,返回值的计算结果是参数中其中一个CompletableFuture
的计算结果applyToEither
:只是判断两个CompletableFuture
,返回值的计算结果要经过fn处理的。Random rand = new Random();
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "abc";
});
//f.get() 所有future运行完才继续往下运行
//CompletableFuture f = CompletableFuture.allOf(future1,future2);
运行结果有时是100,有时是"abc"。
CompletableFuture<Object> f = CompletableFuture.anyOf(future1,future2);
System.out.println(f.get());
CompletableFuture有两个完成方法:
public boolean complete(T value);
public boolean completeExceptionally(Throwable ex);
//mayInterruptIfRunning设成false话,不允许在线程运行时中断,设成true的话就允许。
public boolean cancel(boolean mayInterruptIfRunning);
complete
:将future状态置为已完成,并且将参数注入,但如果这个future已经完成了,则不会产生任何变化
completeExceptionally
:将future状态置为已完成,并且将异常参数注入,并且在get的时候回获取这个异常,但如果这个future已经完成了,则不会产生任何变化
public boolean isDone();
public boolean isCancelled();
public boolean isCompletedExceptionally();
isDone
: 用来返回 future 对象是否已经完成
isCancelled
:用来返回 future 对象是否已经被取消
isCompletedExceptionally
: 用来返回 future 是否出现了异常