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.