一、背景

jdk8中加入了实现类CompletableFuture,用于异步编程。底层做任务使用的是ForkJoin, 顾名思义,是将任务的数据集分为多个子数据集,而每个子集,都可以由独立的子任务来处理,最后将每个子任务的结果汇集起来。它是ExecutorService接口的一个实现,它把子任务分配给线程池(称为ForkJoinPool)中的工作线程。从api文档看,它实现了2个接口CompletionStage和Future。CompletionStage支持lambda表达式,接口的方法的功能都是在某个阶段得到结果后要做的事情。因此,CompletableFuture不仅拥有Future的所有特性,而且还内置了lambda表达式,支持异步回调,结果转换等功能,它有以下Future实现不了的功能:

  1. 合并两个相互独立的异步计算的结果

  2. 等待异步任务的所有任务都完成

  3. 等待异步任务的其中一个任务完成就返回结果

  4. 任务完成后调用回调方法

  5. 任务完成的结果可以用于下一个任务。

  6. 任务完成时发出通知提供原生的异常处理api

二、代码

package com.example.demo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*; public class CompletableFutureDemo {
//CPU核数
private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(AVAILABLE_PROCESSORS,
3 * AVAILABLE_PROCESSORS,
3, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(20)); public static void main(String[] args) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("demo start....." + startTime);
demo3();
System.out.println("demo end.....costTime = " + (System.currentTimeMillis() - startTime));
} /**
* 基于allOf,并行处理多个任务,等待所有任务执行完毕后返回
*/ public static void demo3() throws Exception {
//用户整体接收各个任务的返回值
Map<String,String> dataMap = new ConcurrentHashMap<>();
List<CompletableFuture<String>> futureList = new ArrayList<>();
futureList.add(doSomethingA("A", dataMap));
futureList.add(doSomethingB("B", dataMap));
futureList.add(doSomethingC("C", dataMap));
CompletableFuture<Void> result = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
try {
result.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("dataMap = " + dataMap);
//结果为:{doSomeThingB=B, doSomeThingA=A}
} /**
* 基于thenCompose,第一个任务执行完后,第二个任务使用第一个任务的返回作为参数
*/
public static void demo1() throws Exception {
Map<String,String> dataMap = new HashMap<>();
CompletableFuture<String> completableFuture = doSomethingA("A", dataMap)
.thenCompose(id -> doSomethingB(id, dataMap));
String result = completableFuture.get(3, TimeUnit.SECONDS);
System.out.println("result = " + result);
//结果为:A is done is done } /**
* 基于thenCombine,当两个任务都完成后,使用两者的结果作为参数再执行一个异步任务
*/
public static void demo2() throws Exception {
Map<String,String> dataMap = new HashMap<>();
CompletableFuture<String> completableFuture = doSomethingA("A", dataMap)
.thenCombine(doSomethingB("B", dataMap), (a, b) -> a + " - " + b);
String result = completableFuture.get(3, TimeUnit.SECONDS);
System.out.println("result = " + result);
//结果为:A is done - B is done
} /**
* @param dataMap 用户整体接收方法的返回值
* @return
*/
public static CompletableFuture<String> doSomethingA(String taskId, Map<String,String> dataMap) {
System.out.println("doSomethingA start....." + System.currentTimeMillis());
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
dataMap.put("doSomeThingA", "A");
System.out.println(taskId + " is done and dataMap"+dataMap);
return taskId + " is done";
}, threadPoolExecutor);
} public static CompletableFuture<String> doSomethingB(String taskId, Map<String,String> dataMap) {
System.out.println("doSomethingB start....." + System.currentTimeMillis());
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
dataMap.put("doSomeThingB", "B");
System.out.println(taskId + " is done and dataMap"+dataMap);
return taskId + " -> B is done";
}, threadPoolExecutor);
} public static CompletableFuture<String> doSomethingC(String taskId, Map<String,String> dataMap) {
System.out.println("doSomethingC start....." + System.currentTimeMillis());
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
dataMap.put("doSomeThingC", "C");
System.out.println(taskId + " is done and dataMap"+dataMap);
return taskId + " is done";
}, threadPoolExecutor); } }

三、效率比较

很明显,异步更快

package com.example.demo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture; /**
* @author d00018641
* @date 2021/11/4 15:10
*/
public class TestDemo2 {
private static final String key = "llllllllllllllllllllllll";
public static void main(String[] args) { List<String> requestList = new ArrayList<>();
requestList.add("3");
requestList.add("4");
requestList.add("5");
requestList.add("6");
// 响应参数list
String[] returnArray = new String[requestList.size()];
// 异步查询每一列,定义响应列数的futures
List<CompletableFuture<String>> futures = new ArrayList<>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < requestList.size(); i++) {
final int a = i;
CompletableFuture<String> tf = CompletableFuture.supplyAsync(() -> {
return calc(requestList.get(a));
}).whenComplete((m, e) -> returnArray[a] = m);
futures.add(tf);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
//CompletableFuture end.....costTime = 147
System.out.println("CompletableFuture end.....costTime = " + (System.currentTimeMillis() - startTime));
long startTime1 = System.currentTimeMillis();
for(int i = 0; i < requestList.size(); i++){
returnArray[i] = calc(requestList.get(i));
}
//连续 end.....costTime = 432
System.out.println("连续 end.....costTime = " + (System.currentTimeMillis() - startTime1));
System.out.println(Arrays.asList(returnArray)); } private static String calc(String source) {
int as = Integer.parseInt(source);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return String.valueOf(Math.pow(as, 3));
}
}

四、编程实战

Map<String, ExchangeRateVO> cacheMap = new ConcurrentHashMap<>();
List<CompletableFuture<Void>> batchFutureList = new ArrayList<>();
for (ExchangeRateVO vo : paramList) {
CompletableFuture<Void> batchItem = CompletableFuture.runAsync(() -> {
List<CompletableFuture<Void>> itemFutureList = new ArrayList<>();
for (LookupItemVO toCurrency : toCurrencyList) {
CompletableFuture<Void> cfItem = CompletableFuture.runAsync(() -> {
// 查询接口返回汇率数据
ExchangeRateVO resultVo;
try {
buildBasicContext();
resultVo = iExchangeRateService.findExchangeRate(vo.getCountryCode(), vo.getFromCurrency(),
toCurrency.getItemCode(), vo.getStartDate());
if (CommonUtils.isNotEmpty(resultVo)) {
if (CommonUtils.isEmpty(resultVo.getFromCurrency())) {
resultVo.setFromCurrency(vo.getFromCurrency());
resultVo.setToCurrency(toCurrency.getItemCode());
resultVo.setCountryCode(vo.getCountryCode());
}
resultVo.setStartDate(vo.getStartDate());
resultList.add(resultVo);
cacheMap.put(vo.getCountryCode() + vo.getFromCurrency() + toCurrency.getItemCode(),
resultVo);
}
} catch (Exception er) {
LOGGER.error("findExchangeRate has some error:", er);
}
});
itemFutureList.add(cfItem);
}
CompletableFuture.allOf(itemFutureList.toArray(new CompletableFuture[itemFutureList.size()])).join();
});
batchFutureList.add(batchItem);
}
LOGGER.info2("before submit completable");
CompletableFuture.allOf(batchFutureList.toArray(new CompletableFuture[batchFutureList.size()])).join();

JDK8的异步处理方式-CompletableFuture的使用的更多相关文章

  1. Java8 异步编排类CompletableFuture

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. https://www.cnblogs.com/shijiaqi1066/p/8758206 ...

  2. 异步技巧之CompletableFuture

    摘自--https://juejin.im/post/5b4622df5188251ac9766f47 异步技巧之CompletableFuture 1.Future接口 1.1 什么是Future? ...

  3. Java 异步编程 (5 种异步实现方式详解)

    ​ 同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现@mikechen 目录 什么是异步? 一.线程异步 二.Future异步 三.Comp ...

  4. js中同步与异步请求方式

    异步请求方式: $.ajax({ url : 'your url', data:{name:value}, cache : false, async : true, type : "POST ...

  5. iOS开发——网络编程Swift篇&(六)异步Post方式

    异步Post方式 // MARK: - 异步Post方式 func asynchronousPost() { //创建NSURL对象 var url:NSURL! = NSURL(string: &q ...

  6. iOS开发——网络编程Swift篇&(四)异步Get方式

    异步Get方式 // MARK: - 异步Get方式 func asynchronousGet() { //创建NSURL对象 var url:NSURL! = NSURL(string: " ...

  7. Delphi中ADO异步执行方式

    当ADO开始处理数据后,应用程序必须等到ADO处理完毕之后才可以继续执行.但是除了同步执行方式之外,ADO也提供了异步执行的方式,允许当ADO处理时,应用程序仍然能够先继续执行.而当ADO处理数据完毕 ...

  8. 前端Mahsup异步依赖方式不能做业务数据依赖

    很久之前流行mashup方式做内容集成,之前为了IP定位的方便,引用了第三方的IP定位JS,然后根据其内容与服务器同步地址数据并写入Cookie,可是这种方式一旦,第三方的库反应缓慢时,就会出现大问题 ...

  9. 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第四节:同步与异步请求方式

    前两节,我们对WebRequest和WebResponse这两个类做了介绍,但两者还相对独立.本节,我们来说说如何将两者结合起来,方式有哪些,有什么不同. 1.4.1 说结合,无非就是我们如何发送一个 ...

  10. 【JDK8】Java8 优雅的异步调用API CompletableFuture

    1.CompletableFuture是什么? CompletableFuture是JDK8的新特性之一,是异步调用相关的API,用于简化异步调用,提高异步调用的效率 2.CompletableFut ...

随机推荐

  1. Vue2 常见问题汇总及解决方案

    参考:https://mp.weixin.qq.com/s/6Wapb1bZLQaYv0zlip6ygg 1.安装超时(install timeout) 方案: cnpm : 国内对npm的镜像版本 ...

  2. 钉钉获取群openConversationId方式以及企业内部机器人发送群消息

    https://open-dev.dingtalk.com/apiExplorer#/jsapi?api=biz.chat.chooseConversationByCorpId 通过上述URL进入钉钉 ...

  3. Cannot read properties of null (reading ‘insertBefore‘)

    一.报错现象 vue3 + element plus 项目,本地启动时,页面进行所有操作都正常:部署到test环境后,数据驱动DOM变化的操作会导致如下报错. 二.可能原因及解决方案 经过分析出现报错 ...

  4. JS form表单数据校验及失效情况下的解决方案

    如下图,当执行提交操作之前,我们需要对序号,要求完成时间,责任人,措施内容四项进行非空,字符长度及输入内容的类型进行校验. 直接贴样式代码 <div class="wrapper an ...

  5. JAVA 【SM2】加密解密

    JAVA [SM2]加密解密 前言:最近项目中必须用到SM2的加密解密 引入的Maven依赖 <dependency> <groupId>cn.hutool</group ...

  6. GET请求数据量大造成的问题

    在实际的开发过程中,我们偶尔或者遇到过要导出列表中所有的数据.假设列表中有十万条数据,那么导出所有,意味着要大批量的走查询接口,通常我们的后台的API接口GET请求支持的查询长度不得大于1000,(比 ...

  7. ubuntu拨号上网与以太网

    拨号上网使用的是ppp协议,主机和主机之间是点对点连接的,通常使用掩码255.255.255.255的方式来表示一个点对点连接. 以太网:使用的以太网协议. 拨号上网和以太网是两个完全不同的东西,如果 ...

  8. (0514)python学习-思维导图

  9. @Configuration 配置类打断点后,一启动项目读取到该配置类的话就会进断点

    @Configuration 配置类的话,打断点的时候,一启动项目就会读取配置信息,然后你在@Configuration 配置的类中打断点的话,一启动项目就会读取配置类,然后就会进断点,跟你平常的co ...

  10. docker 运行环境

    步骤 1 - 启用适用于 Linux 的 Windows 子系统 需要先启用"适用于 Linux 的 Windows 子系统"可选功能,然后才能在 Windows 上安装 Linu ...