CompletableFuture
一、异步任务创建
辅助工具类
import java.util.StringJoiner;
public class SmallTool {
public static void sleepMillis(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void printTimeAndThread(String tag){
String result = new StringJoiner("\t|\t")
.add(String.valueOf(System.currentTimeMillis()))
.add(String.valueOf(Thread.currentThread().getId()))
.add(String.valueOf(Thread.currentThread().getName()))
.add(tag)
.toString();
System.out.println(result);
}
}
1.1、runAsync
分析源码:依靠创建一个Runnable() 接口实现类 对象 返回 CompletableFuture<Void>
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
底层是通过开启一个线程去执行
例如:
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程程序开始 执行");
// 使用实现Runnable 匿名内部类
CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
System.out.println("异步线程执行异步任务");
}
});
System.out.println("主线程程序执行 结束 ");
}
用Lambda 简写
public static void main(String[] args) throws InterruptedException {
System.out.println("程序开始 执行");
// 使用实现Runnable 匿名内部类
CompletableFuture.runAsync(() -> {
System.out.println("异步线程执行异步任务");
});
// 让主线程睡3秒,等待一下异步任务执行
Thread.sleep(3000);
System.out.println("程序执行 结束 ");
}
1.2、supplyAsync
实现一个或传入一个 Supplier<U> 供给者,返回一个 CompletableFuture<U>
U 传入的对象类型
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
例如:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("主线程 开始执行");
CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {
String threadName = Thread.currentThread().getName();
String st = "异步任务";
System.out.println("正在执行异步任务的子线程:" + threadName);
return st;
});
// 会阻塞异步任务线程 等待获取到结果
String result = newFuture.get();
System.out.println("异步结果:" + result);
System.out.println("主线程 执行结束");
}
1.3 异步任务线程池
runAsync() 和 supplyAsync() 方法都是在单独的线程池中执行,CompletableFuture 会从全局的线程池 ForkJoinPool.commonPool() 获取线程执行任务
我们也可以创建一个线程池 传入到里面,指定线程池
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
例如
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("主线程 开始执行");
// 创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {
String threadName = Thread.currentThread().getName();
String st = "异步任务";
System.out.println("正在执行异步任务的子线程:" + threadName);
return st;
},executorService);
// 会阻塞异步任务线程 等待获取到结果
String result = newFuture.get();
System.out.println("异步结果:" + result);
executorService.shutdown();
System.out.println("主线程 执行结束");
}
二、异步任务回调
很多时候 我们当一个异步任务处理完后 在对返回的结果进行处理,所以我们把一些回调函数附加到 CompletableFuture
2.1、thenApply
public <U> CompletableFuture<U> thenApply(
Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
接受一个类型为T的Function 然后处理转换 返回 一个 CompletableFuture<U> 类型为U
T :上一个任务返回结果类型
U : 当前任务的返回值类型
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("一个异步任务");
SmallTool.sleepMillis(2000);
return "发起一个http请求 得到的响应值";
});
// 回调这个 异步任务
CompletableFuture<String> Futureresult = newFuture.thenApply(content -> {
SmallTool.printTimeAndThread("回调任务");
//对上一个异步任务转换处理 在返回 一个CompletableFuture
String ss = "加处理";
return content + ss;
});
// 获取异步结果
SmallTool.printTimeAndThread(Futureresult.get());
SmallTool.printTimeAndThread("程序执行 结束");
}
注:
- supplyAsync()会使异步任务在其他线程执行
- thenApply()会使回调任务在当前线程继续执行 ,如果 supplyAsync()立即返回执行结果,则thenApply()会在主线程执行
- 如果在主线程中调用thenApply(),回调任务会在主线程执行,与异步任务线程不同
- 要使异步任务和回调任务在同一线程执行,使用thenApplyAsync()或在异步任务线程中首次调用thenApply()
链式操作,对上一个任务进行 转换
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("一个异步任务");
SmallTool.sleepMillis(1000);
return "发起一个http请求 得到的响应值";
}).thenApply(content -> {
SmallTool.printTimeAndThread("回调转换");
return content + "回调转换";
});
SmallTool.printTimeAndThread("中间");
// 获取异步结果
String s = newFuture.get();
SmallTool.printTimeAndThread(s);
SmallTool.printTimeAndThread("程序执行 结束");
}
2.2、thenAccept
源码
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
例如:链式操作 作为最终消费
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("一个异步任务");
SmallTool.sleepMillis(1000);
return "发起一个http请求 得到的响应值";
}).thenApply(content -> {
SmallTool.printTimeAndThread("回调转换");
return content + "回调转换";
}).thenAccept( (res) -> {
SmallTool.printTimeAndThread("最终的消费");
});
// 主线程加一些等待 否则可能会因主线程结束 看到子线程执行其他的
SmallTool.sleepMillis(3000);
SmallTool.printTimeAndThread("程序执行 结束");
}
2.3、thenRun
如果我们只是想从上一步 CompletableFuture 任务结束后 发送一个通知,也不想使用上一步的结果,thenRun 比较不错的选择
源码:
public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}
例如:可以作用在链式操作的末端
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("一个异步任务");
SmallTool.sleepMillis(1000);
return "发起一个http请求 得到的响应值";
}).thenRun(()->{
SmallTool.printTimeAndThread("异步任务已执行完毕 发送通知");
});
// 主线程加一些等待 否则可能会因主线程结束 看到子线程执行其他的
SmallTool.sleepMillis(3000);
SmallTool.printTimeAndThread("程序执行 结束");
}
2.4、回调提升并行化
我们可以指定异步任务回调 在另外一个 线程中执行,提高并行能力
thenApplyAsync
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);
}
thenAcceptAsync
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
return uniAcceptStage(asyncPool, action);
}
thenRunAsync
public CompletableFuture<Void> thenRunAsync(Runnable action) {
return uniRunStage(asyncPool, action);
}
public CompletableFuture<Void> thenRunAsync(Runnable action,
Executor executor) {
return uniRunStage(screenExecutor(executor), action);
}
三、异步任务编排
3.1、thenCompose
同时有两个任务 有一定依赖,需要第一个任务后的结果传入另一个任务 然后最终返回一个的 CompletableFuture
两个 CompletableFuture 合并为一个最后的 CompletableFuture 返回
和 thenApply 区别
thenApply 返回的是一个结果值 U , 而 thenCompose 返回的还是一个 CompletableFuture
public <U> CompletableFuture<U> thenCompose(
Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(null, fn);
}
例如
public static CompletableFuture<Integer> getCount(){
return CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("发起一个http请求 获取价格");
SmallTool.sleepMillis(2000);
return 200;
});
}
public static CompletableFuture<Integer> youHuiToCount( Integer counts){
return CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("发起一个http请求 获取优惠 然后减免");
SmallTool.sleepMillis(2000);
return counts - 50;
});
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
CompletableFuture<Integer> endCount = getCount().thenCompose(res -> {
return youHuiToCount(res);
});
// 返回的是一个 CompletableFuture
System.out.println(endCount.get());
}
异步
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);
}
3.2、thenCombine
连接两个非依赖任务 ,希望在两个Future 在独立运行完成后,执行回调操作
源码
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 main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
CompletableFuture<Integer> getCount = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("发起一个http请求 获取价格");
SmallTool.sleepMillis(2000);
Integer count = 200;
return count;
});
CompletableFuture<Integer> getYouHui = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("发起一个http请求 获取价格");
SmallTool.sleepMillis(2000);
Integer youHui = 50;
return youHui;
});
CompletableFuture<Integer> combineFuture = getCount.thenCombine(getYouHui, (count, youhui) -> {
SmallTool.printTimeAndThread("计算最终价格");
return count - youhui;
});
Integer join = combineFuture.join();
SmallTool.printTimeAndThread(String.valueOf(join));
SmallTool.printTimeAndThread("程序执行结束");
}
3.3、 allOf
合并多个异步任务,当多个任务都独立完成后 在进一步 消费转换处理
public static CompletableFuture<String> getHttp(String parmes){
return CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("发起一个http请求 "+ parmes);
SmallTool.sleepMillis(2000);
return parmes+"响应";
});
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
// 有多个请求
List<String> requests = Arrays.asList("请求1", "请求2", "请求3");
// 加工为每个任务 存入list集合
List<CompletableFuture<String>> requestFutureList = requests.stream().map(request -> {
return getHttp(request);
}).collect(Collectors.toList());
// 把list集合转成数组
int len = requestFutureList.size();
CompletableFuture[] completableFutures = requestFutureList.toArray(new CompletableFuture[len]);
// 使用allOf 方法合并多个异步任务
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(completableFutures);
// 当所有任务完成后 进行转换处理
CompletableFuture<Long> endAllOfFuture = allOfFuture.thenApply(v -> {
return requestFutureList.stream()
.map(future -> future.join())
.count();
});
// 获取最终的转换处理后的结果
Long join = endAllOfFuture.join();
System.out.println(join);
SmallTool.printTimeAndThread("程序执行结束");
}
3.4 、anyOf
给定的多个异步任务中 如果其中有任意一个Future 执行完成时 需要执行一些操作
会返回一个新的 CompletableFuture 而这个新的结果和 cfs 中已完成那个相同 , 而其中类型我们时未知的 CompletableFuture<Object>
public static CompletableFuture<String> getHttp(Integer parmes){
return CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("发起一个http请求 "+ parmes);
SmallTool.sleepMillis(parmes);
return parmes+"响应";
});
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
SmallTool.printTimeAndThread("程序开始执行");
// 模拟不相同 多个任务请求时间
List<Integer> requests = Arrays.asList(2000, 4000, 10000);
// 加工为每个任务 存入list集合
List<CompletableFuture<String>> requestFutureList = requests.stream().map(request -> {
return getHttp(request);
}).collect(Collectors.toList());
// 把list集合转成数组
int len = requestFutureList.size();
CompletableFuture[] completableFutures = requestFutureList.toArray(new CompletableFuture[len]);
// 使用anyOf 获取最先完成的任务
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(completableFutures);
// 获取最终的转换处理后的结果
Object o = anyOfFuture.get();
System.out.println(o);
SmallTool.printTimeAndThread("程序执行结束");
}