异步编程 - 07 基于JDK中的Future实现异步编程(下)_当Stream遇见CompletableFuture

news2024/9/28 9:28:57

文章目录

  • JDK8 Stream
  • Stream遇见CompletableFuture
  • 小结

在这里插入图片描述


JDK8 Stream

JDK8中提供了流式对数据进行处理的功能,它的出现允许我们以声明式方式对数据集合进行处理。所谓声明式是相对于我们平时所用的命令式编程来说的,使用声明式编程会让我们对业务的表达更清晰。另外使用流可以让我们很方便地对数据集进行并行处理。

比如下面的代码,我们从person列表中过滤出年龄大于10岁的人,并且收集对应的name字段到list,然后统一打印处理。在使用非Stream的情况下,我们会使用如下代码来实现。

public static List<Person> makeList() {
    List<Person> personList = new ArrayList<Person>();
    Person p1 = new Person();
    p1.setAge(10);
    p1.setName("zlx");
    personList.add(p1);

    p1 = new Person();
    p1.setAge(12);
    p1.setName("jiaduo");
    personList.add(p1);

    p1 = new Person();
    p1.setAge(5);
    p1.setName("ruoran");
    personList.add(p1);
    return personList;
}
    
    public static void noStream(List<Person> personList) {

    List<String> nameList = new ArrayList<>();

    for (Person person : personList) {
        if (person.age >= 10) {
            nameList.add(person.getName());
        }
    }
        
    for(String name: nameList) {
        System.out.println(name);
    }

}

    public static void main(String[] args) {

        List<Person> personList = makeList();
        
        noStream(personList);

    }

从上述代码可知,noStream方法是典型的命令式编码,我们用for循环来一个个判断当前person对象中的age字段值是否大于等于10,如果是则把当前对象的name字段放到手动创建的nameList列表中,然后再开启新的for循环逐个遍历nameList中的name字段。

下面我们使用Stream方式来修改上面的代码。

public static void useStream(List<Person> personList) {

    List<String> nameList = personList.stream().filter(person -> person.getAge() >= 10)// 1.过滤大于等于10的age字段值
            .map(person -> person.getName())// 2.使用map映射元素
            .collect(Collectors.toList());// 3.收集映射后元素

    nameList.stream().forEach(name -> System.out.println(name));
}

在上面的代码中我们首先从personList获取到流对象,然后在其上进行了filter运算,过滤出年龄大于等于10的person,然后运用map方法映射person对象到name字段,再使用collect方法收集所有的name字段到nameList,最后从nameList上获取流并调用forEach进行打印。

上面的代码就是声明式编程,其可读性很强,代码直接可以说明想要什么(从代码就可以知道我们要过滤出年龄大于等于10岁的人,并且把满足条件的person的name字段收集起来,然后打印)。

需要注意的是,这里的filter和map操作是中间操作符,也就是当我们在流上施加这些操作时并不会真的被执行。而collect操作是终端操作符,当在流上执行终端操作符时,流上施加的操作才会执行。

JDK8中对于Steam提供了很多操作符,只是简单的列出了filter、map、collect这几种方法。


Stream遇见CompletableFuture

下面我们来看看当Stream与CompletableFuture相结合时会产生什么样的火花。

首先我们来看一个需求,这个需求是消费端对服务提供方集群中的某个服务进行广播调用(轮询调用同一个服务的不同提供者的机器),正常同步调用代码如下所示。

public class StreamTestFuture {

    public static String rpcCall(String ip, String param) {

        System.out.println(ip + " rpcCall:" + param);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return param;

    }

    public static void main(String[] args) {

        // 1.生成ip列表
        List<String> ipList = new ArrayList<String>();
        for (int i = 1; i <=10; ++i) {
            ipList.add("192.168.0." + i);
        }

        // 2.发起广播调用
        long start = System.currentTimeMillis();
        List<String> result = new ArrayList<>();
        for (String ip : ipList) {
            result.add(rpcCall(ip, ip));
        }

        // 3.输出
        result.stream().forEach(r -> System.out.println(r));
        System.out.println("cost:" + (System.currentTimeMillis() - start));
    }
  • 代码1生成ip列表,这代表了所有服务提供者的机器ip。

  • 代码2轮询每个ip,使用ip作为参数调用rpcCall方法(这里面使用休眠1s来模拟远程rpc过程执行)并且把结果保存到result中。

  • 代码3则等所有服务调用完成后打印执行结果,运行上面代码时会发现耗时大概为10s,这是因为代码2发起广播调用是顺序的,也就是当上次rpc调用返回结果后才会进行下一次调用。

下面我们借用Stream和CompletableFuture来看看业务线程如何并发地发起多次rpc请求,从而缩短整个处理流程的耗时。

   // 1.生成ip列表
        List<String> ipList = new ArrayList<String>();
        for (int i = 1; i <= 10; ++i) {
            ipList.add("192.168.0." + i);
        }

        // 2.并发调用
        long start = System.currentTimeMillis();
        List<CompletableFuture<String>> futureList = ipList.stream()
                .map(ip -> CompletableFuture.supplyAsync(() -> rpcCall(ip, ip)))//同步转换为异步
                .collect(Collectors.toList());//收集结果

       //3.等待所有异步任务执行完毕
        List<String> resultList = futureList.stream()
                                           .map(future -> future.join())
                                             //同步等待结果
                                           .collect(Collectors.toList());
                                             //对结果进行收集

        // 4.输出
        resultList.stream().forEach(r -> System.out.println(r));

        System.out.println("cost:" + (System.currentTimeMillis() - start));
  • 代码2从ipList处获取了stream,然后通过map操作符把ip转换为远程调用。

  • 注意,这里通过使用CompletableFuture.supplyAsync方法把rpc的同步调用转换为了异步,也就是把同步调用结果转换为了CompletableFuture对象,所以操作符map返回的是一个CompletableFuture,然后collect操作把所有的CompletableFuture对象收集为list后返回。

  • 此外,这里多个rpc调用时是并发执行的,不是顺序执行,因为CompletableFuture.supplyAsync方法把rpc的同步调用转换为了异步。

  • 代码3从futureList获取流,然后使用map操作符把future对象转换为future的执行结果,这里是使用future的join方法来阻塞获取每个异步任务执行完毕,然后返回执行结果,最后使用collect操作把所有的结果收集到resultList。

  • 代码4从resultList获取流,然后打印结果。

  • 运行上面的代码会发现耗时大大减少了,这可以证明上面10个rpc调用时是并发运行的,并不是串行执行。

注意:具体这10个rpc请求是否全部并发运行取决于CompletableFuture内线程池内线程的个数,如果你的机器是单核的或者线程池内线程个数为1,那么这10个任务还是会顺序执行的。


小结

我们了解了CompletableFuture如何解决其缺点,以及CompletableFuture与JDK Stream是如何完美结合的,可知使用CompletableFuture实现异步编程属于声明式编程,一般情况下不需要我们显式创建线程池并提交任务到线程池,这大大减轻了编程者的负担。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/984836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

意向客户的信息获取到底是怎样的,快来get一下

客户信息获取技术真的可以为企业提供精准客源吗&#xff1f;这个渠道到底安不安全&#xff0c;技术到底成不成熟&#xff1f;效果到底如何&#xff1f;下面简单的和大家分析一下。 客户信息获取技术是怎样的 手机采集引流方面&#xff0c;上量不精准&#xff0c;精准不上量的说…

《Web安全基础》05. XSS · CSRF · SSRF · RCE

web 1&#xff1a;XSS1.1&#xff1a;简介1.2&#xff1a;防护与绕过1.2.1&#xff1a;HttpOnly1.2.2&#xff1a;WAF 绕过 1.3&#xff1a;相关资源 2&#xff1a;CSRF3&#xff1a;SSRF4&#xff1a;RCE 本系列侧重方法论&#xff0c;各工具只是实现目标的载体。 命令与工具只…

什么人群适合考PMP?

PMP是不受行业限制的&#xff0c;只要工作涉及到管理&#xff0c;或者想要往管理方面发展&#xff0c;或者想提升自身的能力&#xff0c;那么考PMP绝对是有必要的。像IT、电子、通信、工程、金融、房产、石化、数据管理、军工、航天等几乎涵盖所有。各行各业都会用的到。 PMP证…

爬虫逆向实战(28)-某税网第一步登录(sm2、sm4、HMacSHA256)

一、数据接口分析 主页地址&#xff1a;某税网 1、抓包 通过抓包可以发现登录接口是factorAccountLogin 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看载荷模块可以发现有一个datagram 和 一个signature加密参数 请求头是否加密&#xff1f; 通过查看“标…

nvm管理(切换)node版本,方便vue2,vue3+ts开发

使用nvm切换node版本 1. 完全删除之前的node及npm&#xff08;清理干净Node: 应用程序&#xff0c;缓存的文件&#xff0c;环境变量 &#xff09; 2. 使用管理员身份安装nvm&#xff0c;下载如下 3. 安装完nvm之后找到nvm下载路径对应的文件 4. 使用管理员身份打开cmd&#xff…

中缀表达式转后缀表达式(逆波兰式)

方法一&#xff1a;加括号法示例 步骤&#xff1a; 1、根据运算符的优先级对中缀表达式加括号&#xff08;有几个运算符就有几对括号&#xff0c;原有的括号不用加&#xff09; 2、将运算符移到对应括号后面 3、去掉所有括号&#xff0c;即为后缀表达式 以下面的中缀表达式为…

2023 年全国大学生数学建模A题目-定日镜场的优化设计

A题目是个典型的优化问题 大致思路&#xff08;非完整&#xff09; 先说题目的模型&#xff0c;有点类似一个人拿着镜子&#xff0c;这个镜子最终要映射到某个点上&#xff0c;有点类似下面这个玩意儿&#xff0c;只不过是个大型的而已 规划的吸收塔类似这个烧水壶&#xff0c;…

纷享销客华为云 | 聚力前行 共创云上新价值

近日&#xff0c;由华为云联合上万家生态伙伴举行的“第二届828 B2B企业节”在深圳盛大开幕&#xff0c;纷享销客作为华为云的战略合作伙伴出席了本次企业节的开幕&#xff0c;并斩获“华为云创新中心优秀制造解决方案伙伴”等多个奖项。纷享销客联合创始人 & 经营中心副总…

CTFHUB ICS(3)

1.S7协议恶意攻击分析 因为是plc的关机&#xff0c;我们直接过滤s7comm协议的内容 然后是提供info的排序我们可以根据这个进行分析 在plc中ack_data是对于job的响应这里ack_data返回了plc的stop即题目中描述的plc关机&#xff0c;而关机的发出属于job的信息所以我们去查找job里…

java八股文面试[设计模式]——创建型模式

创建型模式的作用就是创建对象&#xff0c;说到创建一个对象&#xff0c;最熟悉的就是 new 一个对象&#xff0c;然后 set 相关属性。但是&#xff0c;在很多场景下&#xff0c;我们需要给客户端提供更加友好的创建对象的方式&#xff0c;尤其是那种我们定义了类&#xff0c;但…

29 | 聊聊性能测试的基本方法与应用领域

并发用户数、响应时间、系统吞吐量之间的关系 当系统并发用户数较少时&#xff0c;系统的吞吐量也低&#xff0c;系统处于空闲状态&#xff0c;这个阶段被称为 “空闲区间”。 并发用户数进一步增长&#xff0c;系统的处理能力逐渐趋于饱和&#xff0c;因此每个用户的响应时间会…

2023全国大学生数学建模竞赛A题思路模型代码

目录 一.选题建议先发布&#xff0c;思路模型代码论文第一时间更新&#xff0c;获取见文末名片 二.选题建议&#xff0c;后续思路代码论文 A 题 定日镜场的优化设计 各题分析 获取完整思路代码见此处名片 一.选题建议先发布&#xff0c;思路模型代码论文第一时间更新&…

五大优化技巧,让你的视频直播app源码更加流畅

优化技巧一&#xff1a;性能调优 视频直播app源码在确保流畅体验方面是至关重要的。为了提升性能&#xff0c;以下是几项关键的优化技巧&#xff1a; 使用轻量级编码器和解码器&#xff1a;选择高效的编码器和解码器&#xff0c;以减少资源占用&#xff0c;并确保视频流畅播放…

2023全国大学生数学建模ABCDE选题建议,思路模型,小白要怎么选?难度怎么样

首先最重要的&#xff0c;难度C<B<A&#xff0c;D、E题推荐选E题 大家可以查看我们的视频讲解&#xff0c;在这里&#xff1a;【2023全国大学生数学建模竞赛选题建议&#xff0c;难度分析&#xff0c;小白应该怎么选】 https://b23.tv/S6O26uc 选题建议视频播放​b23.t…

Matlab如何导入Excel数据并进行FFT变换

如果你发现某段信号里面有干扰&#xff0c;想要分析这段信号里面的频率成分&#xff0c;就可以使用matlab导入Excel数据后进行快速傅里叶变换&#xff08;fft&#xff09;。 先直接上使用方法&#xff0c;后面再补充理论知识。 可以通过串口将需要分析的数据发送到串口助手&a…

【1++的数据结构】之哈希(一)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的数据结构】 文章目录 一&#xff0c;什么是哈希&#xff1f;二&#xff0c;哈希冲突哈希函数哈希冲突解决 unordered_map与unordered_set 一&#xff0c;什么是哈希&#xff1f; 首先我们要…

(3)MyBatis-Plus待开发

常用注解 TableName MyBatis-Plus在确定操作的表时&#xff0c;由BaseMapper的泛型决定即实体类型决定&#xff0c;且默认操作的表名和实体类型的类名一致,如果不一致则会因找不到表报异常 //向表中插入一条数据 Test public void testInsert(){User user new User(null, &…

Android 10.0 禁用adb shell input输入功能

1.前言 在10.0的产品开发中,在进行一些定制开发中,对于一些adb shell功能需要通过属性来控制禁止使用input 等输入功能,比如adb shell input keyevent 响应输入事件等,所以就需要 熟悉adb shell input的输入事件流程,然后来禁用adb shell input的输入事件功能,接下来分…

yolov7添加注意力机制

yolov7结构图 方法:直接在common里改,在相关的后面加上就行 1、接受通道数的注意力机制 1、目的:在三个输出地方添加注意力 yolov7.yaml文件,换成其他模块 注意力链接 2、models下建SE.py 3、common.py下,先找class Conv,再复制一份修改,把模块导进来 4、yolo.…

生成式AI时代的新基础设施

生成式人工智能席卷了科技行业。 2023 年第一季度&#xff0c;随着数亿用户采用 ChatGPT 和 GitHub CoPilot 等应用程序&#xff0c;对新一代 AI 初创公司的投资高达 1.7B 美元。 技术领先的公司正在争先恐后地制定自己的生成式AI策略&#xff0c;许多公司都在努力将应用程序投…