CompletableFuture使用
在之前多线程的文章中会遇到一些问题:
while(!fs.isDone());//Future返回如果没有完成,则一直循环等待,直到Future返回完成
System.out.println(fs.get()); //打印各个线程(任务)执行的结果
1)若Future返回没有完成,当前主线程是堵塞的,主线程业务会一直等着。
2)轮询调用isDone,不优雅。
解决上述问题并应对更多的需要:
1)很多个异步线程执行时间不一致,主线程业务不需要一直等着,可能只等最快的线程执行完或者最重要的任务执行完,或者等待固定的时间(比如几秒钟,防止调用超时),至于没返回结果的线程我就用默认值代替.
2)两个异步任务之间执行独立,但是第二个依赖第一个的执行结果。
CompletableFuture提供了函数式编程能力,使代码更加美观优雅,而且可以通过回调的方式计算处理结果,对异常处理也有了更好的处理手段。
CompletableFuture类四个静态方法执行异步任务:
- 执行有返回值的任务了,方法的入参如果没有传入Executor对象将会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。在实际使用中,一般使用自己创建的线程池对象来作为参数传入使用,提高线程速度。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..}
- 用于执行没有返回值的任务,因为它的入参是Runnable对象。
public static CompletableFuture<Void> runAsync(Runnable runnable){..}
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)){..}
获取执行结果的几个方法
- Future中的实现方式,get()会堵塞当前的线程,如果执行线程迟迟没有返回数据,get()会一直等待下去。抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)。
V get();
- 可以设置等待的时间。
V get(long timeout,Timeout unit);
- 当有了返回结果时会正常返回,如果异步线程抛了异常会返回设置的默认值。
T getNow(T defaultValue);
- 功能和 get() 方法是一样的,都是阻塞获取值,它们的区别在于 join() 抛出的是 unchecked Exception,不会强制开发者抛出,会将异常包装成CompletionException异常 /CancellationException异常,但是本质原因还是代码内存在的真正的异常。Exception in thread “main” java.util.concurrent.CompletionException: java.lang.ArithmeticException:
T join();
thenAccept()
- 功能:当前任务的执行结果可以作为下一任务的输入参数,无返回值。
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);
- 使用场景:执行任务A,待任务A正常返回之后,用A的返回值执行任务B。
public static void main(String[] args) {
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
return "A";
});
futureA.thenAccept(A -> {
System.out.println("打印依赖的线程返回值-"+ A);
});
}
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
- 使用场景:执行任务A,待任务A正常返回之后,用A的返回值执行任务B,下一个任务的执行依赖上一个任务的结果,每个任务都有输入和输出。
public class MyCompletableFuture {
public static void main(String[] args) {
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
return "A";
});
CompletableFuture<String> futureB = futureA.thenApply(A -> {
System.out.println("打印依赖的线程返回值-"+ A);
return "B";
});
}
}
- 以上功能实现,我们可以先调用future.join()先得到任务A的返回值,然后再拿返回值做入参去执行任务B,而thenApply的存在就在于简化了这一步,我们不必因为等待一个计算完成而一直阻塞着调用线程,而是告诉CompletableFuture何时执行完就进行下一步,将多个任务串联起来了。
thenRun(…)
- 功能:不关心上一步的计算结果,上一步执行完以后执行下一个操作。
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
- 场景:先执行任务A,任务A执行完以后执行任务B,任务B不接受任务A的返回值(不管A有没有返回值),无返回值。
/**
* @creater keke
* @time 2021/1/22 15:29
* @description
*/
public class MyCompletableFuture2 {
public static void main(String[] args) {
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
System.out.println("执行任务A");
return "A";
});
futureA.thenRun(() -> System.out.println("执行任务B"));
}
}
thenCombine(…) runAfterBoth(…)
- 功能:结合两个CompletionStage的结果,进行转化后返回
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)
- 场景:future1 和future2 相互独立,当都执行完毕的时候,使用方法:thenCombine(…)将两个任务的结果一块交给 thenCombine 来处理。
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "world");
CompletableFuture<String> result = future1.thenCombine(future2, (t, u) ->
t + " " + u
);
System.out.println(result.get());
}
thenAcceptBoth(…)
- 功能:当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth来进行消耗。
public class MyThenAcceptBoth {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);
future1.thenAcceptBoth(future2, (t, u) ->
System.out.println(t * u));
}
}
applyToEither(…) acceptEither(…) runAfterEither(…)
- 功能:两个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);
- 场景:查询结果有两种方式蓝牙和网络,两种方式查询速度不一致,我们希望哪个先返回就用那个的返回值。
public class MyApplyToEither {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 5);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() ->{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 7;
});
CompletableFuture<Integer> future3 = future1.applyToEither(future2,result -> result);
System.out.println(future3.join());//输出:5
}
}
exceptionally(…)
- 功能:当运行出现异常时,调用该方法可进行一些补偿操作,如设置默认值.
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
- 场景:异步执行任务A获取结果,如果任务A执行过程中抛出异常,则使用默认值"请重试!"返回.
public class MyExceptionally {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "异常:" + 100 / 0)
.thenApply(s -> "f1 result:" + s)
.exceptionally(e -> {
System.out.println(e.getMessage()); //java.lang.ArithmeticException: / by zero
return "f1 result: 请重试!";
});
System.out.println(f1.join());//输出"f1 result: 请重试!"
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "成功!" )
.thenApply(s -> "f2 result:" + s)
.exceptionally(e -> {
System.out.println(e.getMessage()); //java.lang.ArithmeticException: / by zero
return "f2 result: 请重试!";
});
System.out.println(f2.join());//输出"f2 result: 成功!"
}
}
whenComplete(…)
- 功能::当CompletableFuture的结果成功执行,或者抛出异常的时候,都可以进入whenComplete方法执行。
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,Executor executor);
- 功能:当CompletableFuture的计算结果完成,或者抛出异常的时候,都可以进入whenComplete方法执行.
public class MyWhenComplete {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "结果是:" + 100 / 0)
.thenApply(s -> "正确的结果是:" + s)
.whenComplete((s, e) -> {
if (!Objects.isNull(s)) {
System.out.println(s);//未执行
}
if (Objects.isNull(e)) {
System.out.println(s);//未执行
} else {
System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
}
})
.exceptionally(e -> {
System.out.println(e.getMessage());
return "请重试!";
}
);
System.out.println(f1.join());//请重试!
}
}
-
根据控制台,我们可以看出执行流程是这样,supplyAsync->whenComplete->exceptionally,,可以看出并没有进入thenApply执行,可见,thenApply只有当正常返回时才会去执行,而whenComplete不管是否正常执行都会进入。还要注意一点,whenComplete是没有返回值的。
-
上面代码我们先调用whenComplete再调用exceptionally。如果我们先调用exceptionally再调用whenComplete会发生什么呢。结果是输出“返回结果是:请重试!请重试!”
public class MyWhenComplete {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "结果是:" + 100 / 0)
.thenApply(s -> "正确的结果是:" + s)
.exceptionally(e -> {
System.out.println(e.getMessage());
return "请重试!";
}
)
.whenComplete((s, e) -> {
if (Objects.isNull(e)) {
System.out.println("返回结果是:"+s);//返回结果是:请重试!
} else {
System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
}
});
System.out.println(f1.join());//请重试!
}
}
handle(…)
- 功能::当CompletableFuture的计算结果完成,或者抛出异常的时候,可以通过handle方法对结果进行处理。
- handle和whenComplete的区别
1.都是对结果进行处理,handle有返回值,whenComplete没有返回值
2.由于1的存在,使得handle多了一个特性,可在handle里实现exceptionally的功能
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);
- 场景:用handle实现whenComplete+exceptionally的组合功能。
public class MyHandle {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "结果是:" + 100 / 0)
.thenApply(s -> "正确的结果是:" + s)
.handle((s,e)->{
if(Objects.isNull(e)){
System.out.println(s);//未执行
}else{
System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero
}
return "结果是:" + (s == null ? "请重试" : s);
})
.exceptionally(e-> e.getMessage());//未执行
System.out.println(f1.join());//输出”结果是:请重试“
}
}
allOf(…) anyOf(…)
- 功能:
1.allOf:当所有的CompletableFuture都执行完后执行计算
2.anyOf:最快的那个CompletableFuture执行完之后执行计算
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
- 场景:根据货物id分别查询订单,支付,物流,积分。
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000 + RandomUtils.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "订单详情";
},executorService)
.whenComplete((s,e)-> System.out.println(s));
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
return "支付信息";
},executorService)
.whenComplete((s,e)-> System.out.println(s));;
CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> {
return "物流信息";
},executorService)
.whenComplete((s,e)-> System.out.println(s));;
CompletableFuture<String> futureD = CompletableFuture.supplyAsync(() -> {
return "积分信息";
},executorService)
.whenComplete((s,e)-> System.out.println(s));;
CompletableFuture.allOf(futureA, futureB, futureC, futureD);
/*输出:
“支付信息
物流信息
积分信息
订单详情”*/
executorService.shutdown();
}