一、异步操作
- 异步调用可以理解为实现一个可无需等待被调用函数的返回值而让操作继续运行的方法。
- 做过android的小伙伴都知道,在android中复杂或IO操作,均需要放在子线程中执行(主线程必须渲染界面,如果做复杂操作,则会造成界面卡顿),然后子线程完成后以消息的形式通知主线程(ui线程)渲染界面,支持的框架有很多,如AsyncTask、RXJAVA等。
- 在做服务端后端,也会有类似的需求,如多线程,同时请求多个接口,用以降低计算效率,JDK5有提供了Futrue接口,虽然提供了异步操作的能力,但对于结果的获取,却是只能通过future.get()的阻塞方式获取结果,显然这样违背了异步操作的初衷。
- 在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
说明 | 相关方法 |
创建异步任务 | runAsync、supplyAsync |
上一个任务完成后,调用该方法 | thenRun、thenAccept、whenComplete、thenApply、handle 、thenCompose |
异常 | exceptionally |
组合 | thenCombine、thenAcceptBoth、runAfterBoth、applyToEither、acceptEither、runAfterEither |
多任务组合 | allOf、anyOf |
取消 | complete |
二、基本用例
1、runAsync 和 supplyAsync 异步创建,
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)
两者区别:
- supplyAsync 有返回值
- runAsync 无返回值
//无返回值
public static void runAsync() throws Exception {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("run end ,threadName =" + Thread.currentThread().getName());
});
log.info("阻塞等待,threadName =" +Thread.currentThread().getName() +",get ="+ future.get());
}
//有返回值
public static void supplyAsync() throws Exception {
CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("run end ,threadName =" + Thread.currentThread().getName());
return System.currentTimeMillis();
});
log.info("阻塞等待,threadName =" +Thread.currentThread().getName() +",get ="+ future.get());
}
2、 上一个任务完成后,回调
thenRun、thenAccept、whenComplete、thenApply、handle、thenCompose
//无入参,无返回值,(不关心前置任务的处理结果)
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
//接收任务的处理结果,并消费处理,无返回结果。
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)
//类似thenAccept(Consumer<T>) ,只不过增加了一个异常处理的功能。传入的参数类似于handle,如果上一个任务出了异常,
//则传入的T类型为null,Throwable为抛出的异常;如果正常运行,则T类型为之前任务运行后的返回值,Throwable为null
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)
//有入参,有返回值 ,Function参数,T类型是上一个结点传入,需要被消费的值,U类型是生成的,会被输出的值。
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)
//类似于thenApply和exceptionally的结合,如果上一个任务出了异常,则传入的T类型为null,
//Throwable为抛出的异常;如果正常运行,则T类型为运行后的返回值,Throwable为null
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)
// thenCompose,流水线操作,允许你对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
// thenCompose 和 thenApply 很像,区别在于thenCompose返回的是CompletableFuture,是一个你自己已经包装好的对象;而thenApply返回的是值;
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) {
public static void thenRun() throws Exception {
CompletableFuture<Void> future = CompletableFuture.runAsync(() ->
log.info("runAsync=" + Thread.currentThread().getName())
).thenRun(() ->
log.info("thenRun=" + Thread.currentThread().getName()));
future.get();
}
// thenAccept接收任务的处理结果,并消费处理,无返回结果。
public static void thenAccept() throws Exception {
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync=" + Thread.currentThread().getName());
//int i = 10 / 0;
return new Random().nextInt(10);
}).thenAccept(integer -> {
log.info("thenAccept=" + Thread.currentThread().getName() + ",integer=" + integer);
});
log.info("get = " + future.get());
}
public static void whenComplete() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync start = " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//int i = 10 /0;
return "success";
});
future.whenComplete((v, t) -> {
if (t == null) {
log.info("whenComplete v= " + v);
} else {
log.error("whenComplete t = " + t.getMessage());
}
});
log.info(future.get());
}
private static void thenApply() throws Exception {
CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
long result1 = new Random().nextInt(100);
log.info("supplyAsync1 result1= " + result1 + ",threadName = " + Thread.currentThread().getName());
//int i = 10 / 0;
return result1;
}).thenApply((l) -> {
long result2 = l * 5;
log.info("thenApply result2= " + result2 + ",threadName = " + Thread.currentThread().getName());
return result2;
});
log.info(String.valueOf(future.get()));
}
//handle 是执行任务完成时对结果的处理。
//handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。
// thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
public static void handle() throws Exception {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync");
int i = 10 / 0;
return new Random().nextInt(100);
}).handle((param, throwable) -> {
log.info("handle");
//int i= 10/0;
if (throwable == null) {
return param * 2;
} else {
log.error(throwable.getMessage());
return -1;
}
});
log.info("" + future.get());
}
//thenCompose,允许你对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
public static void thenCompose() throws ExecutionException, InterruptedException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync1 = " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "test";
});
CompletableFuture<String> result = future1.thenCompose((params) -> {
log.info("params = "+params);
return CompletableFuture.supplyAsync(() -> {
log.info("new supplyAsync");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "new test";
});
});
log.info("get = "+result.get());
}
3、异步操作代码块出现异常,自动回调action
若未添加exceptionally方法处理,则future.get() 时也会抛出相同异常。
若添加exceptionally方法处理,则exceptionally 方法将捕获异常,且该方法的返回值将代替原来的返回值。
//当前面的任务出了异常时,就会返回T值;否则还是返回原先前面任务应该返回的值。请看下面的代码:
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
//异步操作代码块出现异常,自动回调异常action
public static void exceptionally() throws Exception
{
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync start = " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
throw new NullPointerException();
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
}).exceptionally((t) -> {
log.error("执行失败,errmsg = "+t.getMessage()+"threadName =" + Thread.currentThread().getName());
return null;
});
TimeUnit.SECONDS.sleep(2);
}
4、组合,两个任务组合,完成后进入回调。
thenCombine、thenAcceptBoth、runAfterBoth。
//将两个任务的结果做入参,有返回值
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) 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)
//将两个任务结果做入参,无返回值
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)
//无入参、无返回
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)
//将两个任务执行完毕后,将结果一同传入回调,其中一个出现异常,则都不会进入 apply 方法
private static void combination() throws Exception {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync1 = " + Thread.currentThread().getName());
return "hello";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
//int i = 0 / 0;
log.info("supplyAsync1 = " + Thread.currentThread().getName());
return "world";
});
// //1、thenCombine 等两个 CompletionStage 的任务都完成后,结果做入参一块交给 thenCombine 来处理。
// CompletableFuture<String> result = future1.thenCombine(future2, (t, u) -> {
// log.info("thenCombine apply = " + Thread.currentThread().getName());
// return t + " " + u;
// }).exceptionally((t) -> {
// log.error("exceptionally = " + t.getMessage() + Thread.currentThread().getName());
// return null;
// });
//
// //2、thenAcceptBoth 类似 thenCombine ,只是无返回值
// CompletableFuture<Void> result = future1.thenAcceptBoth(future2, (t, u) ->
// log.info("thenAcceptBoth apply = " + Thread.currentThread().getName())
// ).exceptionally((t) -> {
// log.error("exceptionally = " + t.getMessage() + Thread.currentThread().getName());
// return null;
// });
//3、runAfterBoth 类似 thenCombine ,只是无入参、无返回值
CompletableFuture result = future1.runAfterBoth(future2, () ->
log.info("runAfterBoth apply = " + Thread.currentThread().getName())
).exceptionally((t) -> {
log.error("exceptionally = " + t.getMessage() + Thread.currentThread().getName());
return null;
});
log.info("get = " + result.get());
}
5、组合2,其中有一个完成了,就将进入任务完成回调
applyToEither、acceptEither、runAfterEither。
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)
//两个任务进行组合,其中有一个完成了,就将进入任务完成回调
// applyToEither / acceptEither / runAfterEither
private static void combination2() throws Exception {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
log.info("supplyAsync1 = " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
//int i = 0 / 0;
log.info("supplyAsync1 = " + Thread.currentThread().getName());
return "world";
});
//1、applyToEither,有入参,有返回值
// CompletableFuture<String> result = future1.applyToEither(future2,(v) -> {
// log.info("result , v = "+ v +",threadName ="+ Thread.currentThread().getName());
// return null;
// });
//2、acceptEither,有入参,无返回值
// CompletableFuture<Void> result = future1.acceptEither(future2,(v) -> {
// log.info("result , v = "+ v +",threadName ="+ Thread.currentThread().getName());
// });
//3、runAfterEither 无入参,无返回值
CompletableFuture<Void> result = future1.runAfterEither(future2, () -> {
log.info("result ," + "threadName=" + Thread.currentThread().getName());
});
result.get();
}
6、多任务allOf和anyOf
并行处理多个任务,等待所有任务执行完毕后返回
//allOf: 等待所有任务执行完成才执行 result,如果有一个任务异常终止,则result.get时会抛出异常,都是正常执行,result.get返回null
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
//anyOf: 返回cfs中第一个执行完成的任务(是指异步任务的代码,不是指回调),其他任务也都会都会继续执行
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
package com.storm.completablefuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import java.util.concurrent.*;
/**
* create by hyq on 2021/4/15
*/
@Slf4j
public class CompletableFutureTest2 {
/**
* CPU核数
*/
private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
private static int taskCount;
// private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
// AVAILABLE_PROCESSORS, //核心线程数
// 3 * AVAILABLE_PROCESSORS, //最大线程数
// 3, TimeUnit.SECONDS, //keepAliveTime
// new LinkedBlockingDeque<>(20)); //阻塞队列
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, //核心线程数
1, //最大线程数
50, TimeUnit.SECONDS, //keepAliveTime
new LinkedBlockingDeque<>(2000)); //阻塞队列
static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) throws Exception {
long startTime = System.currentTimeMillis();
log.info("demo start....." + startTime);
allOfAndAnyOf();
log.info("demo end.....costTime = " + (System.currentTimeMillis() - startTime));
}
/**
* 基于allOf,并行处理多个任务,等待所有任务执行完毕后返回,有异常也继续执行
* 基于anyOf,并行处理多个任务,等待所有任务执行完毕后返回,只要有一个任务异常,就会回调 结束动作。
*/
public static void allOfAndAnyOf() throws Exception {
//用于整体接收各个任务的返回值,注意这个值,我在多线程里面操作,所以用的是hashtable ,支持并发
Map<String, Object> dataMap = new Hashtable<>();
List<CompletableFuture<String>> futureList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
futureList.add(createTask(i + "", dataMap));
}
//1、allOf ,等待所有任务执行完成才执行 result,如果有一个任务异常终止,则result.get时会抛出异常,都是正常执行,result.get返回null
CompletableFuture<Void> result = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
//2.anyOf 返回cfs中第一个执行完成的任务(是指异步任务的代码,不是指回调),其他任务也都会都会继续执行
//CompletableFuture<Object> result = CompletableFuture.anyOf(futureList.toArray(new CompletableFuture[futureList.size()]));
result.whenComplete((a, t) -> {
if (t != null) {
log.error("whenComplete err = " + t.getMessage());
} else {
log.info("dataMap.size =" + dataMap.size());
}
});
log.info("get = " + result.get());
log.info("taskCount = " + taskCount);
log.info("result = " + dataMap.size());
for (String str :dataMap.keySet()) {
log.info(str);
}
TimeUnit.SECONDS.sleep(20);
}
public static CompletableFuture<String> createTask(String taskId, Map<String, Object> dataMap) {
taskCount++;
CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
log.info("TASK statrt ,ID = " + taskId + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Integer.parseInt(taskId) / 10 == 0) {
int i = 10 / 0;
}
return taskId;
}
/*, threadPoolExecutor*/ )
.handle((v,t)->{
if(t == null){
log.info("TASK end,ID = " + v + Thread.currentThread().getName());
dataMap.put("" + v, ""+v);
return v;
}else {
log.error("TASK end,ID = " + v + Thread.currentThread().getName());
return "err";
}
});
return result;
}
}
7、取消任务,complete
get()与join()方法都会阻塞当前线程,如果想中断当前任务,可以通过 complete()方法,结束任务,让阻塞的代码往下执行。
//参数T,用来给get()或join()替换原逻辑的返回值;
//complete 返回的boolean是表示,是否替换了原返回值。假设原异步任务应该返回A,但complete设置了B,则
//当get()获取为A,boolean = false;
//当get()获取为B,boolean = true;
public boolean complete(T value)
public static void join() {
CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
/*循环*/
// infiniteLoop();
log.info("supplyAsync == " + Thread.currentThread().getName());
return "I am Awesome";
}).thenApply((v) -> {
log.info("thenApply == " + Thread.currentThread().getName());
return "thenApply result";
});
// try {
// TimeUnit.SECONDS.sleep(5);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
log.info("complete:" + cf1.complete("Default"));
log.info("isDone:" + cf1.isDone());
log.info("result:" + cf1.join());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}