Java异步执行器CompletableFuture使用详解
一、简介
Future
实际采用FutureTask
实现,该对象相当于是消费者和生产者的桥梁,消费者通过FutureTask
存储任务的处理结果,更新任务的状态:未开始、正在处理、已完成等。而生产者拿到的FutureTask
被转型为Future
接口,可以阻塞式获取任务的处理结果,非阻塞式获取任务处理状态。
CompletableFuture
是JDK8
提供的Future
增强类。CompletableFuture
异步任务执行线程池,默认是把异步任务都放在ForkJoinPool
中执行。
在这种方式中,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。
二、创建异步任务
CompletableFuture
提供了四个静态方法来创建一个异步操作。
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
没有指定Executor
的方法会使用ForkJoinPool.commonPool()
作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。
runAsync
方法不支持返回值。supplyAsync
可以支持返回值。
2.1 runAsync
runAsync
表示创建无返回值的异步任务,相当于ExecutorService submit(Runnable task)
方法。
示例
//无返回值
public static void runAsync() throws Exception {
CompletableFuture<Void> f = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
System.out.println("run end ...");
});
f.get();
}
2.2 supplyAsync
supplyAsync
表示创建带返回值的异步任务的,相当于ExecutorService submit(Callable<T> task)
方法。
示例
//有返回值
public static void supplyAsync() throws Exception {
CompletableFuture<Long> f = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
System.out.println("run end ...");
return System.currentTimeMillis();
});
long time = f.get();
System.out.println("time = " + time);
}
三、结果处理
3.1 whenComplete
whenComplete
是当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null
,回调方法对应的CompletableFuture
的result
和该任务一致,如果该任务正常执行,则get
方法返回执行结果,如果是执行异常,则get
方法抛出异常。
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,
Executor executor);
可以看到Action
的类型是BiConsumer<? super T, ? super Throwable>
它可以处理正常的计算结果,或者异常情况。
whenComplete
和whenCompleteAsync
的区别:
whenComplete
:是执行当前任务的线程执行继续执行whenComplete
的任务。whenCompleteAsync
:是执行把whenCompleteAsync
这个任务继续提交给线程池来进行执行。
示例
public static void whenComplete() throws Exception {
CompletableFuture<Void> f = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
if (new Random().nextInt() % 2 >= 0) {
int i = 12 / 0;
}
System.out.println("run end ...");
});
f.whenComplete(new BiConsumer<Void, Throwable>() {
@Override
public void accept(Void t, Throwable action) {
System.out.println("执行完成!");
}
});
TimeUnit.SECONDS.sleep(2);
}
3.2 exceptionally
exceptionally
方法指定某个任务执行异常时执行的回调方法,会将抛出异常作为参数传递到回调方法中,如果该任务正常执行则会exceptionally
方法返回的CompletionStage
的result
就是该任务正常执行的结果。
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);
示例
public static void exceptionally() throws Exception {
CompletableFuture<Void> f = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
if (new Random().nextInt() % 2 >= 0) {
int i = 12 / 0;
}
System.out.println("run end ...");
});
f.whenComplete(new BiConsumer<Void, Throwable>() {
@Override
public void accept(Void t, Throwable action) {
System.out.println("执行完成!");
}
});
f.exceptionally(new Function<Throwable, Void>() {
@Override
public Void apply(Throwable t) {
System.out.println("执行失败!" + t.getMessage());
return null;
}
});
TimeUnit.SECONDS.sleep(2);
}
3.3 handle
handle
是执行任务完成时对结果的处理。
handle
方法和thenApply
方法处理方式基本一样。不同的是handle
是在任务完成后再执行,还可以处理异常的任务。thenApply
只可以执行正常的任务,任务出现异常则不执行thenApply
方法。
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);
示例
public static void handle() throws Exception {
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int i= 10/0;
return new Random().nextInt(10);
}
}).handle(new BiFunction<Integer, Throwable, Integer>() {
@Override
public Integer apply(Integer param, Throwable throwable) {
int result = -1;
if (throwable == null) {
result = param * 2;
} else {
System.out.println(throwable.getMessage());
}
return result;
}
});
System.out.println(f.get());
}
从示例中可以看出,在handle
中可以根据任务是否有异常来进行做相应的后续处理操作。而thenApply
方法,如果上个任务出现错误,则不会执行thenApply
方法。
四、串行执行
4.1 thenApply
当一个线程依赖另一个线程时,可以使用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);
参数Function<? super T,? extends U>
说明
- T:上一个任务返回结果的类型
- U:当前任务的返回值类型
示例
private static void thenApply() throws Exception {
CompletableFuture<Long> f = CompletableFuture.supplyAsync(new Supplier<Long>() {
@Override
public Long get() {
long result = new Random().nextInt(100);
System.out.println("result1 = " + result);
return result;
}
}).thenApply(new Function<Long, Long>() {
@Override
public Long apply(Long t) {
long result = t * 5;
System.out.println("result2 = " + result);
return result;
}
});
long result = f.get();
System.out.println(result);
}
第二个任务依赖第一个任务的结果。
4.2 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);
示例
public static void thenAccept() throws Exception{
CompletableFuture<Void> f = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
return new Random().nextInt(10);
}
}).thenAccept(integer -> {
System.out.println(integer);
});
f.get();
}
从示例代码中可以看出,该方法只是消费执行完成的任务,并可以根据上面的任务返回的结果进行处理。并没有后续的输出操作。
4.3 thenRun
跟thenAccept
方法不一样的是,不关心任务的处理结果。只要上面的任务执行完成,就开始执行thenAccept
。
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action, Executor executor);
示例
public static void thenRun() throws Exception {
CompletableFuture<Void> f = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
return new Random().nextInt(10);
}
}).thenRun(() -> {
System.out.println("thenRun ...");
});
f.get();
}
该方法同thenAccept
方法类似。不同的是上个任务处理完成后,并不会把计算的结果传给thenRun
方法。只是处理玩任务后,执行thenAccept
的后续操作。
五、组合运算
5.1 thenCompose
thenCompose
方法允许你对两个CompletionStage
进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
Executor executor);
示例
private static void thenCompose() throws Exception {
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
System.out.println("t1 = " + t);
return t;
}
}).thenCompose(new Function<Integer, CompletionStage<Integer>>() {
@Override
public CompletionStage<Integer> apply(Integer param) {
return CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = param * 2;
System.out.println("t2 = " + t);
return t;
}
});
}
});
System.out.println("thenCompose result : " + f.get());
}
5.2 thenCombine
thenCombine
会把两个CompletionStage
的任务都执行完成后,把两个任务的结果一块交给thenCombine
来处理。
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn,
Executor executor);
示例
private static void thenCombine() throws Exception {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
return "hello";
}
});
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
return "hello";
}
});
CompletableFuture<String> result = f1.thenCombine(f2, new BiFunction<String, String, String>() {
@Override
public String apply(String t, String u) {
return t + " " + u;
}
});
System.out.println(result.get());
}
5.3 thenAcceptBoth
当两个CompletionStage
都执行完成后,把结果一块交给thenAcceptBoth
来进行消耗。
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action,
Executor executor);
示例
private static void thenAcceptBoth() throws Exception {
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
f1.thenAcceptBoth(f2, new BiConsumer<Integer, Integer>() {
@Override
public void accept(Integer t, Integer u) {
System.out.println("f1 = " + t + ";f2 = " + u + ";");
}
});
}
5.4 runAfterBoth
两个CompletionStage
,都完成了计算才会执行下一步的操作(Runnable)。
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action,
Executor executor);
示例
private static void runAfterBoth() throws Exception {
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
f1.runAfterBoth(f2, new Runnable() {
@Override
public void run() {
System.out.println("上面两个任务都执行完成了。");
}
});
}
六、Either
acceptEither
、runAfterEither
和applyToEither
这三个方法只取组合线程中执行最快的一个结果。
6.1 acceptEither
两个CompletionStage
,谁执行返回的结果快,就用那个CompletionStage
的结果进行下一步的消耗操作。
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,
Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
Consumer<? super T> action,
Executor executor);
示例
private static void acceptEither() throws Exception {
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
f1.acceptEither(f2, new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println(t);
}
});
}
6.2 applyToEither
两个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);
示例
private static void applyToEither() throws Exception {
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
CompletableFuture<Integer> result = f1.applyToEither(f2, new Function<Integer, Integer>() {
@Override
public Integer apply(Integer t) {
System.out.println(t);
return t * 2;
}
});
System.out.println(result.get());
}
6.3 runAfterEither
两个CompletionStage
,任何一个完成了都会执行下一步的操作(Runnable
)。
public CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action,
Executor executor);
示例
private static void runAfterEither() throws Exception {
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
f1.runAfterEither(f2, new Runnable() {
@Override
public void run() {
System.out.println("上面有一个已经完成了。");
}
});
}
七、辅助方法
allOf
和anyOf
可以组合任意多个CompletableFuture
。
allOf
和anyOf
的区别:
allof
等待所有任务执行完成才执行。anyOf
是只要有一个任务执行完成。
7.1 allOf
allOf
返回的CompletableFuture
是多个任务都执行完成后才会执行,只要有一个任务执行异常,则返回的CompletableFuture
执行get
方法时会抛出异常,如果都是正常执行,则get
返回null
。
返回值是CompletableFuture
类型,这是因为每个传入的CompletableFuture
的返回值都可能不同,所以组合的结果是无法用某种类型来表示的,索性返回Void
类型。
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
示例
private static void allOf() throws Exception {
// 创建异步执行任务
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
// allOf等待所有任务执行完成才执行f3,如果有一个任务异常终止,则f3.get时会抛出异常,都是正常执行,f3.get返回null
CompletableFuture f3 = CompletableFuture.allOf(f1, f2).whenComplete((a, b) -> {
if (b != null) {
System.out.println("error stack trace ->");
b.printStackTrace();
} else {
System.out.println("run succ,result ->" + a);
}
});
}
示例2
并行下载100
个网页,待下载完成之后,统计在100
个网页中,含有某个单词的网页个数。
// url列表集合
List<String> webPageLinks = Arrays.asList("...");
// 并行下载
List<CompletableFuture> pageFutures = webPageLinks.stream()
.map(webPageLink -> downLoadWebPage(webPageLink))
.collect(Collectors.toList());
// 通过allOf,等待所有网页下载完,收集返回结果
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
pageFutures.toArray(new CompletableFuture[pageFutures.size()])
);
示例3
多个线程异步调用远程接口,最后汇总结果统一返回。
//线程池定义
private static final ExecutorService executorService = new ThreadPoolExecutor(1, 5, 60,
TimeUnit.MINUTES, new ArrayBlockingQueue<>(512));
//具体处理逻辑
@Override
public List<Order> getDataListByBatch(OrderQueryDo dataQuery, int totalSize) {
//防止并发操作情况下出现 并发修改异常,单线程不存在这个问题,CopyOnWriteArrayList底层使用Lock锁,性能可以被保证
List<Order> dataList = new CopyOnWriteArrayList<>();
//获取总页数
int totalPage = getTotalPage(totalSize, dataQuery.getRows());
//获取页数
List<Integer> pageNumbers = getPageNumbers(totalPage);
log.info("getDataListByBatch() -> request: {}, totalSize: [{}], totalPage: [{}]",
JSON.toJSONString(dataQuery), totalSize, totalPage);
//计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//异步回调
CompletableFuture[] completableFutures = pageNumbers.stream()
.map(pageNumber -> CompletableFuture.supplyAsync(() -> {
//执行分页查询
dataQuery.setPage(pageNumber);
Result<PageInfo<Order>> listPage = selectPage(dataQuery);
//返回数据
return Objects.nonNull(listPage.getData())
? listPage.getData().getRecords()
: new ArrayList<Order>();
}, executorService).whenComplete((bodyList, throwable) -> { //处理结果
if (!ObjectUtils.isEmpty(bodyList)) {
//将数据添加到list中
dataList.addAll(bodyList);
}
})).toArray(CompletableFuture[]::new);
//所有任务执行完才放行
CompletableFuture.allOf(completableFutures).join();
stopWatch.stop();
log.info("getDataListByBatch() -> stopWatch: {}ms", stopWatch.getTotalTimeMillis());
return dataList;
}
/**
* 处理总页数
*
* @param totalSize
* @return
*/
private int getTotalPage(int totalSize, int row) {
return (totalSize % row == 0)
? (totalSize / row)
: (totalSize / row + 1);
}
/**
* 获取页数
*
* @param totalPage
* @return
*/
private List<Integer> getPageNumbers(int totalPage) {
int pageNumber = 1;
List<Integer> pageNumbers = Lists.newArrayList();
while (pageNumber <= totalPage) {
pageNumbers.add(pageNumber++);
}
return pageNumbers;
}
7.2 anyOf
anyOf
方法的参数是多个给定的CompletableFuture
,当其中的任何一个完成时,方法返回这个CompletableFuture
。
但由于每个CompletableFuture
的返回值类型都可能不同,任意一个,意味着无法判断是什么类型,所以anyOf
的返回值是CompletableFuture
类型。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
示例
private static void anyOf() throws Exception {
// 创建异步执行任务
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f1 = " + t);
return t;
}
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int t = new Random().nextInt(3);
try {
TimeUnit.SECONDS.sleep(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f2 = " + t);
return t;
}
});
// anyOf是只有一个任务执行完成,无论是正常执行或者执行异常,都会执行f3,f3.get的结果就是已执行完成的任务的执行结果
CompletableFuture<Object> f3 = CompletableFuture.anyOf(f1, f2).whenComplete((a, b) -> {
if (b != null) {
System.out.println("error stack trace ->");
b.printStackTrace();
} else {
System.out.println("run succ, result ->" + a);
}
});
}
在该例子中,因为f1
、f2
的返回值都是CompletableFuture
,所以anyOf
的返回的Object
一定也是Integer
类型。