目录
一、概述
CompletableFuture是jdk8的新特性。CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步会点、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。
二、创建异步任务
1.Async结尾的表示异步执行任务,supply开头的表示有返回值,run无返回值
// 带返回值异步请求,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 带返回值的异步请求,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
// 不带返回值的异步请求,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 不带返回值的异步请求,可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
2.测试
package com.atguigu.java;
import org.junit.Test;
import java.util.concurrent.*;
public class ThreadTest {
public static ThreadPoolExecutor executor= new ThreadPoolExecutor(
5,
10,
10,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//测试创建有返回值的异步任务
@Test
public void test1() throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("当前线程计算结束" + i);
return i;
}, executor);
//等待任务执行完成
System.out.println("结果->" + Future1.get());
System.out.println("main...end...");
}
//测试创建有返回值的异步任务
@Test
public void test2() throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture<Void> Future1 = CompletableFuture.runAsync(() -> {
int i = 10 / 2;
System.out.println("当前线程计算结束" + i);
}, executor);
//等待任务执行完成
System.out.println("结果->" + Future1.get());
System.out.println("main...end...");
}
}
3.获取任务结果的方法
// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException
// 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
// 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
// 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex)
三、完成时回调方法
1.whenComplete可以处理正常返回的结果和异常信息
exceptionally 处理异常信息并支持返回
public CompletableFuture<T> whenComplete(
BiConsumer<? super T, ? super Throwable> action)
//void accept(T t, U u);
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)
//R apply(T t);
测试
//测试完成时回调方法及异常处理返回
@Test
public void test3() throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 0;
System.out.println("当前线程计算结束" + i);
return i;
}, executor).whenCompleteAsync((result,exception)->{
System.out.println("上个任务结果:" + result);
System.out.println("上个任务抛出异常:" + exception);
System.out.println(Thread.currentThread() + " test3 do something....");
},executor).exceptionally(exception ->{
return 0;
});
System.out.println( Future1.get());
//等待任务执行完成
System.out.println("main...end...");
}
2.handle和handleAsync
跟whenComplete基本一致,区别在于handle的回调方法有返回值可以改变返回值。
public <U> CompletableFuture<U> handle(
BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
//R apply(T t, U u);
测试:
//测试handle方法
@Test
public void test4() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 0;
System.out.println("当前线程计算结束" + i);
return i;
}, executor).handleAsync((result,exception)->{
//有结果直接返回结果 有异常返回0
if (result != null){
return result;
}
if (exception != null){
return 0;
}
return 0;
},executor);
System.out.println( Future1.get());
//等待任务执行完成
System.out.println("main...end...");
}
四、线程串行化方法
1.thenRun方法:只要上面点的方法前执行完成,就开始执行thenRun后的方法
不会拿上个任务的返回值 并且后面的任务也没有返回值
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
2.thenAccept方法:会拿上个任务的返回结果进行消费,但是后面的任务,无返回结果
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
//void accept(T t);
3.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)
//R apply(T t);
测试:
//测试thenRun方法
@Test
public void test5() throws Exception {
System.out.println("main...start...");
CompletableFuture<Void> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("当前线程计算结束" + i);
return i;
}, executor).thenRunAsync(()->{
System.out.println("任务2启动了");
}, executor);
System.out.println( Future1.get());
//等待任务执行完成
System.out.println("main...end...");
}
//测试thenAccept方法
@Test
public void test6() throws Exception {
System.out.println("main...start...");
CompletableFuture<Void> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("当前线程计算结束" + i);
return i;
}, executor).thenAcceptAsync((result)->{
System.out.println("任务2启动了,拿到任务1的计算结果为"+result);
}, executor);
System.out.println( Future1.get());
//等待任务执行完成
System.out.println("main...end...");
}
//测试thenApply方法
@Test
public void test7() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("当前线程计算结束" + i);
return i;
}, executor).thenApplyAsync((result)->{
System.out.println("任务2启动了,拿到任务1的计算结果为"+result);
//对任务1的结果再加上1
return result+1;
}, executor);
System.out.println("最后的结果为"+Future1.get());
//等待任务执行完成
System.out.println("main...end...");
}
五、两任务组合 -- 都要完成
这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。
区别:
thenCombine会将两个任务的执行结果作为所提供函数的参数,且该方法有返回值;
thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值;
runAfterBoth没有入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。
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)
// R apply(T t, U u);
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action, Executor executor)
//void accept(T t, U u);
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,
Executor executor)
测试:
//测试runAfterBoth方法
@Test
public void test8() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Void> Future3 = Future1.runAfterBothAsync(Future2, () -> {
System.out.println("任务1和2执行完毕 启动任务3");
}, executor);
//等待任务执行完成
System.out.println("任务3结果->" + Future3.get());
System.out.println("main...end...");
}
//测试thenAcceptBoth方法
@Test
public void test9() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Void> Future3 = Future1.thenAcceptBothAsync(Future2, (result1,result2) -> {
System.out.println("任务1的结果"+result1+"~~~~~ 任务2的结果" + result2);
System.out.println("任务1和2执行完毕 启动任务3");
}, executor);
//等待任务执行完成
System.out.println("任务3结果->" + Future3.get());
System.out.println("main...end...");
}
//测试thenCombine方法
@Test
public void test10() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Integer> Future3= Future1.thenCombineAsync(Future2, (result1, result2) -> {
System.out.println("任务1和2执行完毕 启动任务3");
return result1+result2;
}, executor);
//等待任务执行完成
System.out.println("任务3结果->" + Future3.get());
System.out.println("main...end...");
}
六、两任务组合 -- 一个完成
这三个方法和上面一样也是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。
区别:
applyToEither会将已经完成任务的执行结果作为所提供函数的参数,且该方法有返回值;acceptEither同样将已经完成任务的执行结果作为方法入参,但是无返回值;
runAfterEither没有入参,也没有返回值。
测试
//测试runAfterEitherh方法
@Test
public void test11() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Void> Future3 = Future1.runAfterEitherAsync(Future2, () -> {
System.out.println("任务1和2有一个执行完毕 启动任务3");
}, executor);
//等待任务执行完成
System.out.println("任务3结果->" + Future3.get());
System.out.println("main...end...");
}
//测试acceptEither方法
@Test
public void test12() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Void> Future3 = Future1.acceptEitherAsync(Future2, (result) -> {
System.out.println("最先完成任务的结果"+result);
System.out.println("任务1和2有一个执行完毕 启动任务3");
}, executor);
//等待任务执行完成
System.out.println("任务3结果->" + Future3.get());
System.out.println("main...end...");
}
//测试applyToEither方法
@Test
public void test13() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Integer> Future3= Future1.applyToEitherAsync(Future2, (result) -> {
System.out.println("最先完成任务的结果"+result);
System.out.println("任务1和2有一个执行完毕 启动任务3");
return result;
}, executor);
//等待任务执行完成
System.out.println("任务3结果->" + Future3.get());
System.out.println("main...end...");
}
七、多任务组合
1.allof方法:等待所有任务完成 返回的是null
2.anyof:只要有一个任务完成 可以返回先完成那个任务的结果
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
//测试allof方法
@Test
public void test14() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Integer> Future3 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务3启动");
return i;
}, executor);
CompletableFuture<Void> allOf = CompletableFuture.allOf(Future1, Future2, Future2);
//等待所有任务都完成
System.out.println(allOf.get());
}
//测试anyof方法
@Test
public void test15() throws Exception {
System.out.println("main...start...");
CompletableFuture<Integer> Future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
System.out.println("任务1启动");
return i;
}, executor);
CompletableFuture<Integer> Future2 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2启动");
return i;
}, executor);
CompletableFuture<Integer> Future3 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 5;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务3启动");
return i;
}, executor);
CompletableFuture<Object> anyof = CompletableFuture.anyOf(Future1, Future2, Future2);
//等待一个任务完成
System.out.println(anyof.get());
}
八、业务场景的使用
案例来自尚硅谷
假设要求:
第一,要先拿到商品的基本信息 基本信息里面有 销售id 和 销售规格id
第二,拿到商品基本信息后 可以 根据商品基本信息 异步去获取 促销、销售属性、规格参数等信息
第三,图片信息和商品基本没有上下关联关系 可以同时异步获取
第四,所以信息全部查询完成后一起返回
伪代码:
@Test
public void test16() throws ExecutionException, InterruptedException {
//所有信息的汇总
SkuiVo skuiVo = new SkuiVo();
//查询基本信息(带返回值的异步)
CompletableFuture<Skuinfo> infoFuture = CompletableFuture.supplyAsync(() -> {
//假设查询到了商品基本信息
Skuinfo skuinfo = new Skuinfo();
skuinfo.setSpuId("1");
skuinfo.setSpuId("2");
skuinfo.setGuiId("3");
skuiVo.setSkuinfo(skuinfo);
return skuinfo;
}
, executor);
//查到基本信息后 异步同时去查 促销信息 规格信息 销售属性信息
//拿到查基本信息任务的返回值 任务本身无需返回值
CompletableFuture<Void> saleCxFuture = infoFuture.thenAcceptAsync((res) -> {
String spuId = res.getSpuId();
//拿到商品的销售id后查促销信息
skuiVo.setSaleCx("促销信息");
}, executor);
CompletableFuture<Void> saleSxFuture = infoFuture.thenAcceptAsync((res) -> {
String spuId = res.getSpuId();
//拿到商品的销售id后查销售属性
skuiVo.setSaleSx("销售属性信息");
}, executor);
CompletableFuture<Void> saleGgFuture = infoFuture.thenAcceptAsync((res) -> {
String spuId = res.getSpuId();
String guiId = res.getGuiId();
//拿到商品的销售id和规格id 查商品规格信息
skuiVo.setSaleGg("商品规格信息");
}, executor);
//查基本信息的时候 同时异步 也根据商品id 查商品图片信息
//这个任务不需要返回值
CompletableFuture<Void> imageFuture= CompletableFuture.runAsync(() -> {
//查商品图片信息
skuiVo.setImages("商品图片信息");
}, executor);
//等待所有任务都完成
CompletableFuture.allOf(saleCxFuture,saleSxFuture,saleGgFuture,imageFuture).get();
System.out.println(skuiVo);
}
测试结果:
参考:
1.尚硅谷谷粒商城教学