目录标题
前置知识:函数式接口
Function类
特点:有入参T,有返回值R
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1);
}
Consumer类
特点:有入参T,无返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);
}
Supplier类
特点:无入参,有返回值T
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Predict类
特点:有入参T,有返回值boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T var1);
}
总结
CompletableFuture
在工作中,常常会调用多个服务或者方法去获取不同的数据,如果传统做法就是串行一个个获取,然后封装返回。我们可以尝试使用CompletableFuture,将多个操作交给异步线程执行,然后主线程等待最长任务完成,将所有结果一并返回即可。
1. 创建实例
// 无入参有返回值
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);
我们注意到每种方法都有一个重构的方法。Executor参数可以手动指定线程池,否则默认ForkJoinPool.commonPool()系统级公共线程池。
默认的commonPool的这些线程都是守护线程。我们在编程的时候需要谨慎使用守护线程,如果将我们普通的用户线程设置成守护线程,当我们的程序主线程结束,JVM中不存在其余用户线程,那么CompletableFuture的守护线程会直接退出,造成任务无法完成的问题!!
demo - runAsync不带线程池
主线程执行完毕就结束,无论异步线程任务执行进度
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println("线程[" + Thread.currentThread().getName() + "],结束");
demo - runAsync带线程池
不需要异步任务执行结果,并且保证异步任务可以正常执行完成
//解决方案1: 增加抵定义线程池
CompletableFuture<Void> runWithThreadPool = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行,自定义线程池");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, queryBestThreadPool);
System.out.println("线程[" + Thread.currentThread().getName() + "]结束");
demo - get()阻塞等待结果
需要异步任务执行结果的场景,变成同步
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
runAsync.get();
System.out.println("线程[" + Thread.currentThread().getName() + "]结束");
demo - supplyAsync不带线程池
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "success";
});
//主线程执行完毕,结束
System.out.println("线程[" + Thread.currentThread().getName() + "结束");
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "success";
});
//supplyAsync.get() 会阻塞等待异步任务执行结果,结果返回,主线程打印日志,结束
System.out.println("线程[" + Thread.currentThread().getName() + "],get()="+supplyAsync.get());
demo - supplyAsync带线程池
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "success";
}, queryBestThreadPool);
//主线程走完,结束
System.out.println("线程[" + Thread.currentThread().getName() + "],结束");
//后续异步线程正常执行完成,打印日志
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("线程[" + Thread.currentThread().getName() + "]异步执行");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "success";
}, queryBestThreadPool);
//主线程阻塞等待异步结果,拿到结果后,主线程正常走
System.out.println("线程[" + Thread.currentThread().getName() + "],get()="+supplyAsync.get());
2. whenComplete
考虑当我们在CompletableFuture执行结束的时候,希望能够得到执行结果、或者异常,然后对结果或者异常做进一步处理。那么我们就需要使用到whenComplete。
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//int a = 1 / 0;
System.out.println("线程[" + Thread.currentThread().getName() + "],多线程异步执行完成");
return "success";
//如果这里不加线程池,主线程任务执行完毕,结束。(不会等异步线程,也不会执行到whenComplete代码里)
}, queryBestThreadPool);
//没有返回
//res:上一步执行结果 ex:上一步抛出的异常
CompletableFuture<String> complete = supply.whenComplete((res, ex) -> {
if (Objects.nonNull(ex)) {
ex.printStackTrace();
System.out.println("失败,线程[" + Thread.currentThread().getName() + "],res: " + res + ",throws:" + ex);
} else {
System.out.println("成功,线程[" + Thread.currentThread().getName() + "],res: " + res + ",throws:" + ex);
}
});
//异常以后,不走下面这行代码,get()抛出的异常,如果异常后,有其他的逻辑需要处理,可以将get() try起来~
System.out.println("线程[" + Thread.currentThread().getName() + "],complete.get():" + complete.get() + ",supply.get():" + supply.get());
正常执行
异常执行
3. handle
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
// int a = 1 / 0;
System.out.println("线程[" + Thread.currentThread().getName() + "],多线程异步执行完成");
return "success";
}, queryBestThreadPool);
CompletableFuture<String> complete = supply.handle((res, ex) -> {
if (Objects.nonNull(ex)) {
ex.printStackTrace();
System.out.println("线程[" + Thread.currentThread().getName() + "],异常,res: " + res + ",throws:" + ex);
return null;
} else {
res = res + "重新组装";
System.out.println("线程[" + Thread.currentThread().getName() + "],成功,res: " + res);
return res;
}
});
//异常后,不走下面
System.out.println("线程[" + Thread.currentThread().getName() + "],结束,supply.get()=" + supply.get() + ",complete.get()=" + complete.get());
正常执行
异常执行
4. exceptionally
当发生异常时候的处理,注意异常后返回值类型需要和发生异常的CF返回值类型一致,相当于一种服务降级的思想。
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
int a = 1 / 0;
System.out.println("线程[" + Thread.currentThread().getName() + "],多线程异步执行完成");
return "success";
}, queryBestThreadPool)
.exceptionally(e -> {
System.out.println("线程[" + Thread.currentThread().getName() + "],异常,e="+e);
e.printStackTrace();
return "error";
});
System.out.println("线程[" + Thread.currentThread().getName() + "],结束,supply.get()=" + supply.get());
正常执行
异常执行
5. CompletableFuture组合式Api - allOf
allOf 的返回值是 CompletableFuture类型,这是因为 每个传入的 CompletableFuture 的返回值都可能不同,所以组合的结果是 无法用某种类型来表示的,索性返回 Void 类型。
static void allof(ThreadPoolTaskExecutor queryBestThreadPool) throws Exception {
List<Integer> result = new ArrayList<>();
List<Integer> errorId = new ArrayList<>();
List<Integer> successId = new ArrayList<>();
List<CompletableFuture> taskList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
final Integer temp = i;
CompletableFuture<Integer> supplyAsyncRes = CompletableFuture.supplyAsync(() -> {
//处理核心逻辑
if (temp == 3 || temp == 6 || temp == 9) {
int a = 1 / 0;
}
System.out.println("线程[" + Thread.currentThread().getName() + "],多线程异步执行完成,i:" + temp);
return temp;
}, queryBestThreadPool).whenComplete((res, error) -> {
if (Objects.nonNull(error)) {
errorId.add(temp);
} else {
successId.add(res);
}
});
taskList.add(supplyAsyncRes);
}
CompletableFuture.allOf(taskList.toArray(new CompletableFuture[taskList.size()]));
//阻塞等待任务完成
for (CompletableFuture cf : taskList) {
try {
result.add((Integer) cf.get());
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "异常啦");
e.printStackTrace();
}
}
System.out.println("successId:" + successId);
System.out.println("errorId:" + errorId);
if (CollectionUtils.isNotEmpty(errorId)) {
throw new CustomException(CustomExceptionEnum.FAIL);
}
//成功后的逻辑
System.out.println("线程[" + Thread.currentThread().getName() + "],结束,res:" + result);
前置知识来源:https://endwas.cn/blog/78
demo来源:https://blog.csdn.net/qq_40922616/article/details/121045841