FutureTask的缺点
当我们需要异步调用任务并且需要异步任务的返回值时我们就需要使用FutureTask的get方法来获取返回值.
public static ExecutorService executorService = Executors.newFixedThreadPool(20);
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> integerFutureTask = new FutureTask<>(() -> {
//业务逻辑
return 1;
});
executorService.submit(integerFutureTask);
Integer res = integerFutureTask.get();
System.out.println(res);
System.out.println(123);
}
程序输出
123
1
加入我们的异步任务非常的耗时由于get方法会阻塞主线程就会导致程序的吞吐量下降
public static ExecutorService executorService = Executors.newFixedThreadPool(20);
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> integerFutureTask = new FutureTask<>(() -> {
//业务逻辑
Thread.sleep(2000);
return 1;
});
executorService.submit(integerFutureTask);
Integer res = integerFutureTask.get();
System.out.println(res);
System.out.println(123);
}
程序输出
1
123
必须要等异步任务完成才能继续执行主线程,不仅如此,当我们在碰到一下业务场景的时候,单纯使用Future接口或者FutureTask类并不能很好地完成以下我们所需的业务.
CompletableFuture
CompletableFuture它可以完成复杂的异步任务编程功能,异步编程意味着在主线程之外创建一个独立的线程,与主线程分隔开,并在上面运行一个非阻塞的任务,然后通知主线程进展,成功或者失败。通过这种方式我们的主线程就不需要阻塞等待返回结果。或者以下的业务逻辑也是需要CompletableFuture来完成的。
- 将两个异步计算合并为一个,这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果
- 等待Future集合种的所有任务都完成。
- 仅等待Future集合种最快结束的任务完成(有可能因为他们试图通过不同的方式计算同一个值),并返回它的结果。
- 通过编程方式完成一个Future任务的执行(即以手工设定异步操作结果的方式)。
- 应对Future的完成时间(即当Future的完成时间完成时会收到通知,并能使用Future的计算结果进行下一步的的操作,不只是简单地阻塞等待操作的结果)
创建异步任务
CompletableFuture有两种方法创建异步任务,分别是
supplyAsync执行CompletableFuture任务,支持返回值, 同时支持使用自己的线程池。
runAsync执行CompletableFuture任务,没有返回值,同时支持使用自己的线程池
我的应用
@Test
public void TestFindPrice() {
List<String> list = Arrays.asList("Apple", "Banana");
List<CompletableFuture<String>> priceFuture =
list.stream()
.map(a -> CompletableFuture.supplyAsync(
() -> a + " price is:" + calculatePrice(a)))
.collect(Collectors.toList());
List<String> resultList = priceFuture.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
resultList.forEach(System.out::println);
}
private Double calculatePrice(String product) {
Double value = 0.0;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
switch (product) {
case "Apple":
value = 10.0;
break;
case "Banana":
value = 5.0;
break;
default:
break;
}
return value;
}
/**
* 多线程实现百万导出到excel 50s优化到10s
* @param response
* @throws IOException
*/
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
try {
//获取总数据量
int count = userService.count();
//每页多少个
int pageSize = 10000;
//必须放到循环外,否则会刷新流
ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
List<CompletableFuture> completableFutures = new ArrayList<>();
List<String> completableFutures = new ArrayList<>();
for (int i = 0; i < (count / pageSize) + 1; i++) {
int finalI = i;
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
List<UserEntity> exportList = userService.findLimit(finalI * pageSize, pageSize);
if (!CollectionUtils.isEmpty(exportList)) {
WriteSheet writeSheet = EasyExcel.
writerSheet(finalI, "用户" + (finalI + 1)).head(UserEntity.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
synchronized (excelWriter){
excelWriter.write(exportList, writeSheet);
}
}
},threadPoolExecutor);
completableFutures.add(completableFuture);
}
for (CompletableFuture completableFuture : completableFutures) {
completableFuture.join();
}
//刷新流
excelWriter.finish();
System.out.println("本次导出execl任务执行完毕,共消耗: " + (System.currentTimeMillis() - start) + "ms");
} catch (Exception e) {
throw new RuntimeException(e);
}
outputStream.flush();
response.getOutputStream().close();
}