JUC CompletableFuture 异步编排
使用场景:
当业务逻辑复杂时,特别是在微服务下,有些数据还需要远程调用.多个业务逻辑独立运行将耗费更多的时间.
例如:
- 查询商品基本信息 -> 0.5s
- 查询商品库存信息(远程调用) -> 1s
- 查询商品图片 -> 0.5s
- 查询商品规格属性 (依赖商品基本信息查询结果) -> 0.5s
- 提交所有商品信息 (依赖其他查询结果) -> 1s
所有应用使用单线程顺序执行时将需要2.5s才能完成.如果有多个线程同时操作(需要注意数据的依赖性),也许只需要2.5s.
使用CompletableFuture 异步编排实现场景:
- 分析各个异步任务之间的数据依赖
- 分析各个异步任务是否有返回值
public class CompletableFutureDemo {
//自定义线程池
private static ExecutorService service = new ThreadPoolExecutor(5,
10,
30,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String args[]){
//三个没有数据依赖的可并行线程
CompletableFuture<String> futureBasic = CompletableFuture.supplyAsync(() -> {
System.out.println("商品基本信息对象查询完成");
return "商品基本信息对象";
}, service);
CompletableFuture<String> futureWare = CompletableFuture.supplyAsync(() -> {
//TODO 远程调用库存服务模块
System.out.println("商品库存对象查询完成");
return "商品库存对象";
}, service);
CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
System.out.println("商品图片对象查询完成");
return "商品图片对象";
}, service);
//线程串行化方法 查询商品规格属性 (依赖商品基本信息查询结果)
CompletableFuture<String> futureInfo = futureBasic.thenApplyAsync((basicResult) -> {
//从basicResult 中获取商品基本信息 查询商品规格属性
System.out.println("商品规格属性对象查询完成");
return "商品规格属性对象";
}, service);
//当4个商品对象的信息查询线程全部结束后,开启提交所有商品信息线程
CompletableFuture<Void> futureFinish = CompletableFuture.allOf(futureBasic, futureWare, futureImg, futureInfo);
try {
//等待所有线程结束
futureFinish.get();
CompletableFuture<Void> futureCommit = CompletableFuture.runAsync(() -> {
System.out.println("商品提交");
}, service);
//调用对应CompletableFuture.get方法获取返回值
System.out.println(futureBasic.get());
System.out.println(futureWare.get());
System.out.println(futureImg.get());
System.out.println(futureInfo.get());
}catch(Exception e) {
e.printStackTrace();
}
}
}
运行结果:
商品基本信息对象查询完成
商品库存对象查询完成
商品图片对象查询完成
商品规格属性对象查询完成
商品基本信息对象
商品库存对象
商品图片对象
商品规格属性对象
商品提交
1. 创建异步对象
CompletableFuture提供了四个静态方法来创建一个异步操作
- runAsync 传入Runnable接口参数,不产生返回值
- supplyAsync 传入Supplier供给型接口参数,产生一个返回值
- 两个方法均可以传入自定义的线程池对象 Executor
java8 四大函数式接口
源码解析:
Executor参数接受一个线程池对象:
public interface ExecutorService extends Executor {
Supplier是一个供给型函数式接口:
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
* @return a result
*/
T get();
}
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);
}
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);
}
测试:
1. runAsync(Runnable,Executor):
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
//runAsync
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
int i = 5;
System.out.println(i);
}, executor);
System.out.println("main end ....");
}
}
输出:
main start ....
main end ....
pool-1-thread-1
5
2. supplyAsync(Supplier supplier,Executor executor):
package com.rwp.gulimail.search.thread;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第一次尝试",22);
}, executor);
try {
//阻塞等待,获取结果
Student student = future.get();
System.out.println(student);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main end ....");
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Data
public static class Student{
private String name;
private Integer age;
}
}
输出:
main start ....
CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22)
main end ....
2. 计算完成时回调方法
**作用:**当上一步创建的异步对象成功后可用设置回调方法.
CompletableFuture提供了四个方法来进行计算完成时的回调;用于异步对象完成后执行
- whenComplete 用于异步对象完成后执行, 传入一个具有2个参数的函数型接口BiConsumer<T, U> ,其中T为任意对象,U为异常对象.
- whenCompleteAsync,与whenComplete 功能相同.区别:当异步回调任务完成后以异步的方式将任务转交线程池,由其他线程继续执行后续回调方法.
- whenCompleteAsync方法均可以传入自定义的线程池对象 Executor
- exceptionally方法用来处理异常情况,传入一个函数式接口Function<T, R>,apply传入参数T,返回结果R
- exceptionally和whenComplete 的区别:
- whenComplete 虽然能得到异常信息,但是没法修改返回数据
- exceptionally 可用感知异常,同时返回默认值
源码解析:
Function<T, R>:
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
BiConsumer<T, U>
@FunctionalInterface
public interface BiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
*
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
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);
}
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {
return uniExceptionallyStage(fn);
}
测试:
1.whenComplete:
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future = CompletableFuture.supplyAsync(() ->{
// int error =10 /0;
return new Student("彷徨的我第一次尝试",22);
}, executor).whenComplete((result,exception)->{
if(exception == null){
System.out.println("异步回调成功完成,结果是:"+result+"如果错误:");
}else {
System.out.println("异常是:"+exception);
}
});
System.out.println("main end ....");
}
}
正常运行结果:
main start ....
main end ....
异步回调成功完成,结果是:CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22)
异常运行结果:
main start ....
异常是:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
main end ....
2.exceptionally
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future = CompletableFuture.supplyAsync(() ->{
int error =10 /0;
return new Student("彷徨的我第一次尝试",22);
}, executor).whenComplete((result,exception)->{
//虽然能得到异常信息,但是没法修改返回数据
if(exception == null){
System.out.println("异步回调成功完成,结果是:"+result);
}
}).exceptionally(throwable->{
//感知异常,修改返回数据
//返回结果设置为默认返回
return new Student("默认返回",200);
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main end ....");
}
}
结果:
main start ....
CompletableFutureDemo.Student(name=默认返回, age=200)
main end ....
3. handle方法
**作用:与Complete方法一样,可用对结果做最后的处理(可处理异常),可改变返回值.
类似于**Complete+exceptionally
CompletableFuture提供了三个handle方法:
- 分为异步和同步方法,区别与**Complete方法相同;
- 可以传入一个双参数的函数式接口BiFunction<T, U, R>,apply方法接受T,U两个参数,返回R类型对象
- handleAsync方法均可以传入自定义的线程池对象 Executor
源码解析:
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
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);
}
测试:
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future = CompletableFuture.supplyAsync(() ->{
// int error =10 /0;
return new Student("彷徨的我第一次尝试",22);
}, executor).handle((result,exception) ->{
//产生异常
if(exception != null ){
return new Student("默认返回",200);
}
result.setName("handle二次处理的名字");
return result;
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main end ....");
}
}
正常结果:
main start ....
CompletableFutureDemo.Student(name=handle二次处理的名字, age=22)
main end ....
异常结果:
main start ....
CompletableFutureDemo.Student(name=默认返回, age=200)
main end ....
4. 线程串行化方法
**作用:**将两个线程任务串联执行(例如有数据依赖的任务之间)
CompletableFuture提供的3组9个方法实现线程串行化**
- thenRun*: 指明下一步运行的任务,可以传入一个Runnable接口和Executor线程池(异步情况下Async),不接受上一步线程的返回结果.不产生返回值
- thenAccept*: 指明下一步运行的任务,可以传入一个Consumer消费型接口(接受一个参数T,没有返回值)和Executor线程池(异步情况下Async),接受上一步线程的返回结果T.不产生返回值
- thenApply*: 指明下一步运行的任务,可以传入一个Function函数型接口(接受一个参数T,返回一个R)和Executor线程池(异步情况下Async),接受上一步线程的返回结果T.产生返回值R
源码解析:
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 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 <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);
}
测试:
1.thenRunAsync
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第一次尝试",22);
}, executor);
future1.thenRunAsync(()->{
System.out.println("thenRunAsync 无法获取上一步运行结果");
},executor);
System.out.println("main end ....");
}
}
结果:
main start ....
main end ....
thenRunAsync 无法获取上一步运行结果
2.thenAcceptAsync
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第一次尝试",22);
}, executor);
future1.thenAcceptAsync((result)->{
System.out.println("thenAcceptAsync 可以获取上一步运行结果" +result);
},executor);
System.out.println("main end ....");
}
}
结果:
main start ....
main end ....
thenAcceptAsync 可以获取上一步运行结果CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22)
3.supplyAsync
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第一次尝试",22);
}, executor);
CompletableFuture<Student> future2 = future1.thenApplyAsync(result -> {
System.out.println("thenApplyAsync 可以获取上一步运行结果" + result);
result.setName("被thenApplyAsync修改后的名字");
return result;
}, executor);
try {
System.out.println(future2.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main end ....");
}
}
结果:
main start ....
thenApplyAsync 可以获取上一步运行结果CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22)
CompletableFutureDemo.Student(name=被thenApplyAsync修改后的名字, age=22)
main end ....
5. 两任务组合 -都要完成
**作用:**当两个异步任务必须都完成时,触发该任务.
CompletableFuture提供的3组9个方法实现两
- runAfterBoth*: 指明一个运行的异步任务和传入一个Runnable接口和Executor线程池(异步情况下Async),当当前任务和第一个参数异步任务都完成后,执行Runable接口,不接受上一步线程的返回结果.不产生返回值
- thenAcceptBoth*: 指明一个运行的异步任务和传入一个Consumer消费型接口(接受两个参数T,U为前2个异步任务的返回结果,没有返回值)和Executor线程池(异步情况下Async),当当前任务和第一个参数异步任务都完成后,执行Runable接口,接受上一步线程的返回结果T,U.不产生返回值
- thenApply*: 指明一个运行的异步任务和传入一个Function函数型接口(接受两个参数T,U为前2个异步任务的返回结果,返回一个R)和Executor线程池(异步情况下Async),当当前任务和第一个参数异步任务都完成后,执行Runable接口,接受上一步线程的返回结果T,U.产生返回值R
源码解析:
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);
public <U> CompletionStage<Void> thenAcceptBothAsync
(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action,Executor executor);
public <U,V> CompletionStage<V> thenCombineAsync
(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
测试:
thenCombineAsync:
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第一次尝试",22);
}, executor);
CompletableFuture<Student> future2 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第二次尝试",23);
}, executor);
CompletableFuture<Student> future3 = future1.thenCombineAsync(future2, (t, u) -> {
System.out.println(t + " " + u);
return new Student("彷徨的我第三次尝试", 24);
}, executor);
try {
System.out.println(future3.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main end ....");
}
}
**结果:**当future1和future2都执行完成后,future3才执行
main start ....
CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22) CompletableFutureDemo.Student(name=彷徨的我第二次尝试, age=23)
CompletableFutureDemo.Student(name=彷徨的我第三次尝试, age=24)
main end ....
6. 两任务组合 - 一个完成
**作用:**只要其中一个异步任务完成时,触发该任务.
使用方法与两任务同时完成触发相同
源码:
public <U> CompletableFuture<U> applyToEitherAsync(
CompletionStage<? extends T> other, Function<? super T, U> fn,
Executor executor) {
return orApplyStage(screenExecutor(executor), other, fn);
}
public CompletableFuture<Void> acceptEitherAsync(
CompletionStage<? extends T> other, Consumer<? super T> action,
Executor executor) {
return orAcceptStage(screenExecutor(executor), other, action);
}
public CompletableFuture<Void> acceptEitherAsync(
CompletionStage<? extends T> other, Consumer<? super T> action,
Executor executor) {
return orAcceptStage(screenExecutor(executor), other, action);
}
测试:
applyToEitherAsync:
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第一次尝试",22);
}, executor);
CompletableFuture<Student> future2 = CompletableFuture.supplyAsync(() ->{
return new Student("彷徨的我第二次尝试",23);
}, executor);
CompletableFuture<Student> future3 = future1.applyToEitherAsync(future2, (t) -> {
System.out.println(t + " ");
return new Student("彷徨的我第三次尝试", 24);
}, executor);
try {
System.out.println(future3.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main end ....");
}
}
结果:
main start ....
CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22)
CompletableFutureDemo.Student(name=彷徨的我第三次尝试, age=24)
main end ....
7. 多任务组合
**作用:**多任务进行组合,当一个(anyof)或多个(allof)任务完成时,触发该任务.
CompletableFuture提供两个静态方法实现多任务组合:
- anyof和allof 均可以传递多个CompletableFuture 对象作为参数
- anyof 没有返回值;allof有返回值
源码:
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
return orTree(cfs, 0, cfs.length - 1);
}
测试:
1.allOf
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]) throws ExecutionException, InterruptedException {
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
Student s = new Student("彷徨的我第一次尝试", 22);
System.out.println(s);
return s;
}, executor);
CompletableFuture<Student> future2 = CompletableFuture.supplyAsync(() ->{
Student s = new Student("彷徨的我第二次尝试", 23);
System.out.println(s);
return s;
}, executor);
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(future1, future2);
voidCompletableFuture.get();//等待所有结果完成
System.out.println("main end ....");
}
}
结果:
main start ....
CompletableFutureDemo.Student(name=彷徨的我第一次尝试, age=22)
CompletableFutureDemo.Student(name=彷徨的我第二次尝试, age=23)
main end ....
2.anyof
public class CompletableFutureDemo {
//创建一个基础线大小为10的程池
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String args[]) throws ExecutionException, InterruptedException {
System.out.println("main start ....");
CompletableFuture<Student> future1 = CompletableFuture.supplyAsync(() ->{
Student s = new Student("彷徨的我第一次尝试", 22);
System.out.println(s);
return s;
}, executor);
CompletableFuture<Student> future2 = CompletableFuture.supplyAsync(() ->{
Student s = new Student("彷徨的我第二次尝试", 23);
System.out.println(s);
return s;
}, executor);
CompletableFuture<Object> voidCompletableFuture = CompletableFuture.anyOf(future1, future2);
System.out.println(voidCompletableFuture.get()); //等待一个成功
System.out.println("main end ....");
}
}