目录
为什么用CompletableFuture,异步编程还能用哪些类?
为什么用CompletableFuture,异步编程还能用哪些类?
- Future和Callable
// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 创建多个 Callable 对象
List<Callable<String>> callableTasks = new ArrayList<>();
callableTasks.add(() -> {
Thread.sleep(2000); // 模拟耗时操作
return "Task 1";
});
callableTasks.add(() -> {
Thread.sleep(1000); // 模拟耗时操作
return "Task 2";
});
callableTasks.add(() -> {
Thread.sleep(1500); // 模拟耗时操作
return "Task 3";
});
try {
// 提交多个 Callable 任务并同时等待它们的完成
List<Future<String>> futures = executor.invokeAll(callableTasks);
// 提交单个 Callable 任务并获得 Future 对象
// 创建一个 Callable 对象
// Callable<String> callableTask = () -> {
// Thread.sleep(2000); // 模拟耗时操作
// return "Hello from Callable!";
// };
// Future<String> future = executor.submit(callableTask);
// 处理每个任务的结果
for (Future<String> future : futures) {
String result = future.get();
System.out.println(result);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
缺点:没有回调函数。不支持异常处理。
CompletableFuture怎么用?
CompletableFuture支持回调函数和异常处理,掌握下面14个方法。
- runAsync(Runnable runnable):开启异步运行任务,并且不从任务中返回任何内容。它接受一个Runnable接口实现类对象,方法返回
CompletableFuture<Void>
对象 - supplyAsync(Supplier<U> supplier):开启异步运行任务,会从任务中返回结果。它接收一个
Supplier<U>
供给者,用于供给带返回值的异步任务并返回CompletableFuture<U>
runAsync(Runnable runnable,Executor executor)和supplyAsync(Supplier<U> supplier,Executor executor):还可以接收自定义线程池。否则默认使用ForkJoinPool.commonPool()
线程池。如果所有completableFuture
共享一个线程池,那么一旦有异步任务执行一些很慢的I/O操作,就会导致线程池中所有的线程都阻塞在I/O操作上,从而造成线程饥饿,进而影响整个系统的性能。
- thenApply(Function<? super T,? extends U> fn):异步任务的回调函数,会返回一个新的结果
- thenAccept
(Consumer<T>)
:异步任务的回调函数,不返回结果 - thenRun(Runnable action):异步任务的回调函数,得到一个完成的通知,甚至都不使用上一个链式操作的结果
- 异步回调
-
CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) // 回调方的异步变体(异步回调) CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) // thenAccept和其异步回调 CompletableFuture<Void> thenAccept(Consumer<? super T> action) CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action Executor executor) // thenRun和其异步回调 CompletableFuture<Void> thenRun(Runnable action) CompletableFuture<Void> thenRunAsync(Runnable action) CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
- thenCompose(Function<? super T, ? extends CompletionStage<U>> fn):组合两个有依赖关系的异步任务,结果由第二个任务返回
- thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn):组合两个没有依赖关系的异步任务
- allOf(CompletableFuture<?>... cfs):组合多个异步任务,当所有异步任务都完成时可以进一步操作
- anyOf(CompletableFuture<?>... cfs):组合多个异步任务,当任何一个异步任务完成时就可以进一步操作
- exceptionally(Function<Throwable,? extends T> fn): 用于处理多个连续回调函数上的异常, 回调链上出现的任何异常,回调链不继续向下执行,都在exceptionally中处理异常。
- handle(BiFunction<? super T, Throwable, ? extends U> fn):常被用来恢复多个连续回调函数上的异常,回调链恢复后可继续向下执行。
applyToEither()
把两个异步任务做比较,异步任务先到结果的,就对先到的结果进行下一步操作。acceptEither()
把两个异步任务做比较,异步任务先到结果的,就对先到的结果进行下一步操作(消费使用)。runAfterEither()
:如果不关心最先到达的结果,只想在有一个异步任务完成时得到完成的通知,可以使用
案例:
// 异步处理多个文件
public boolean uploadSfzImg(String yhdm, MultipartFile[] fileArr){
// 创建文件夹
String path = this.getClass().getResource("/").getPath() + File.separator + Constant.FILE_TEMP_PATH+File.separator+ yhdm;
File tempFile = new File(path);
if(!tempFile.exists()){
tempFile.mkdirs();
}
final ExecutorService executorService = Executors.newFixedThreadPool(fileArr.length);
// 每一个文件开启一个异步任务
List<CompletableFuture<Boolean>> fileFutureList = Arrays.stream(fileArr).map(file -> {
CompletableFuture<Boolean> fileFuture = CompletableFuture.supplyAsync(() -> {
try {
file.transferTo(new File(Constant.FILE_TEMP_PATH + File.separator + file.getOriginalFilename()));
} catch (IOException e) {
log.error("上传图片失败:"+file.getOriginalFilename()+ e.getMessage(), e);
return false;
}
return true;
},executorService);
return fileFuture;
}).collect(Collectors.toList());
CompletableFuture[] fileFutureArr = fileFutureList.toArray(new CompletableFuture[fileFutureList.size()]);
// 将多个异步任务合并
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(fileFutureArr);
// 异步任务的回调函数,统计成功的个数
CompletableFuture<Long> count = allOfFuture.thenApply(v -> {
return fileFutureList.stream().map(b -> {
return b.join();
}).filter(b -> b).count();
});
// 关闭线程池
executorService.shutdown();
// 都成功
if(count.join() == fileArr.length){
// todo 批量入库,返回成功
return true;
}else {
// 删除全部上传的文件,返回失败
for (MultipartFile file : fileArr) {
String originalFilename = file.getOriginalFilename();
new File(tempFile + File.separator +Constant.FILE_TEMP_PATH + File.separator + originalFilename).delete();
}
return false;
}
}