CompletableFuture使用教学

news2024/12/25 12:37:01

CompletableFuture使用教学

一、开始一个线程异步执行不需要返回值

通过runAsync方式

//1.不加线程池方式
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            //停顿几秒
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println("hello world");
        System.out.println(completableFuture.get());//null 没有返回值的情况
//2.加线程池方式
		//创建固定线程池(阿里规范建议使用自定义线程池,不能通过Executors来进行创建)
        ExecutorService executors = Executors.newFixedThreadPool(4);//此处偷懒,用此线程池
        CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            //停顿几秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, executors);
        System.out.println(runAsync.get());

二、通过异步方式执行,有返回值

supplyAsync

		//不加线程池方式
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "hello supplyAsync";
        });
        System.out.println(completableFuture.get());
		//加线程池的方式
ExecutorService executors = Executors.newFixedThreadPool(4);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "hello supplyAsync+Executors";
        }, executors);
        System.out.println(completableFuture.get());

ps:get()方法可以获取异步线程执行完后的结果

三.通过whenComplete减少阻塞和轮询(可加线程池,也可不加)

即当异步线程执行结束会接着执行whenComplete()方法,如果执行期间报错会执行exceptionally()方法
ExecutorService threadPool = Executors.newFixedThreadPool(6);
        CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "--副线程");
            int result = new Random().nextInt(10);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("1秒后出结果");
            return result;
        },threadPool).whenComplete((v,e)->{//没有异常  v是值 e是异常情况
            if (e == null){
                System.out.println("计算完成,UpdateValue:"+v);
            }
        }).exceptionally((e)->{//e是异常情况

            e.printStackTrace();
            System.out.println("异常情况:"+e.getCause()+"\t"+e.getMessage());
            return null;
        });

四、实现通过货品在不同平台进行价格搜索进行数据汇总

普通方式实现

public class Case {
    static List<NetMall> list = Arrays.asList(new NetMall("jd"),
            new NetMall("dangdang"),
            new NetMall("taobao"));
    public static List<String> getPrice(List<NetMall> list,String productName){
        return list.stream()
                .map(netMall -> String.format(productName+ " in %s price is %s"
                        ,netMall.getNetMallName()
                        ,netMall.calcPrice(productName)))
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        List<String> list1 = getPrice(list, "mysql");
        for (String s : list1) {
            System.out.println(s);
        }
        long end = System.currentTimeMillis();
        System.out.println("---当前操作时间--costTime:"+(end - start)+"ms");
    }

}
class NetMall{
    private String netMallName;

    public String getNetMallName() {
        return netMallName;
    }
    public NetMall(){}

    public NetMall(String netMallName){
        this.netMallName = netMallName;
    }

    public double calcPrice(String productName){
        try {
            TimeUnit.SECONDS.sleep(1);//此处表示 业务执行所需耗时时间
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);//模拟价格
    }

}

普通方式实现耗时结果:
在这里插入图片描述

completableFuture实现

public class Case {
    static List<NetMall> list = Arrays.asList(new NetMall("jd"),
            new NetMall("dangdang"),
            new NetMall("taobao"));
    public static List<String> getPricesByCompletableFuture(List<NetMall> list,String productName){
        return list.stream().map(netMall -> CompletableFuture.supplyAsync(()->
            String.format(
                    productName+"in %s price is %.2f",
                    netMall.getNetMallName(),
                    netMall.calcPrice(productName)
            ))).collect(Collectors.toList())
                .stream()
                .map(s->s.join())
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
		List<String> list1 = getPricesByCompletableFuture(Case.list, "mysql");
        for (String s : list1) {
            System.out.println(s);
        }
        long end = System.currentTimeMillis();
        System.out.println("---当前操作时间--costTime:"+(end - start)+"ms");
    }

}
class NetMall{
    private String netMallName;

    public String getNetMallName() {
        return netMallName;
    }
    public NetMall(){}

    public NetMall(String netMallName){
        this.netMallName = netMallName;
    }

    public double calcPrice(String productName){
        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);//模拟价格
    }

}

结果耗时:
在这里插入图片描述
相比较能够发现,同时开启三个异步线程,时间仅仅为单个平台查询的时间,大大节省效率!

五、CompletableFuture常用API

1.获得结果和触发计算

public T get() 不见不散,容易阻塞
public T get(long timeout,TimeUnit unit) 过时不候,超过时间会爆异常
public T join() 类似于get(),区别在于是否需要抛出异常
public T getNow(T valueIfAbsent)
立即获取结果不阻塞
	计算完,返回计算完成后的结果
	没算完,返回设定的valueAbsent(直接返回了备胎值xxx)
主动触发计算
	public boolean complete(T value) 是否立即打断get()方法返回括号值(下面代码实现)
public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "abc";
        });
        TimeUnit.SECONDS.sleep(1);
        System.out.println(completableFuture.complete("end")+"\t"+completableFuture.get());
    }

执行结果:true end
解释:执行需要2秒,等待1秒;
complete(默认值)方法会打断执行,如果执行完,则返回结果,如果没有执行完则输出默认值;

2.对计算结果进行处理

(1)thenApply 计算结果存在在依赖关系,使得线程串行化。因为依赖关系,所以一旦有异常,直接叫停。
public static void main(String[] args) {
        CompletableFuture.supplyAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("111");
            return 1024;
        }).thenApply(f->{
            System.out.println("222");
            return f + 1;
        }).thenApply(f->{
            System.out.println("333");
            return f + 1;
//            return f/0; 会报出异常
        }).whenCompleteAsync((v,e)->{
            System.out.println("****v="+v);
        }).exceptionally(e->{
            e.printStackTrace();
            return null;
        });
        System.out.println("----主线程结束--end");
        // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
    }
(2)handle 类似于thenApply,但是有异常的话仍然可以往下走一步。
CompletableFuture.supplyAsync(() -> {
            //暂停几秒钟线程
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("111");
            return 1024;
        }).handle((f,e) -> {
            int age = 10/0;//异常语句
            System.out.println("222");
            return f + 1;
        }).handle((f,e) -> {
            System.out.println("333");
            return f + 1;
        }).whenCompleteAsync((v,e) -> {
            System.out.println("*****v: "+v);
        }).exceptionally(e -> {
            e.printStackTrace();
            return null;
        });
        System.out.println("----主线程结束--end");
        //在222方法中报错 会继续执行输出333
(3).对计算结果进行消费
接收任务的处理结果,并消费处理,无返回结果|消费型函数式接口,之前的是Function
CompletableFuture.supplyAsync(()->{
            return 1;
        }).thenApply(f -> {
            return f+1;
        }).thenApply(f ->{
            return f+3;
        }).thenApply(f->{
            return f+4;
        }).thenAccept(r->{
            System.out.println("r==\t"+r);
        });

补充:Code之任务之间的顺序执行

thenRun

  • thenRun(Runnable runnable)
  • 任务A执行完执行B,并且B不需要A的结果

thenAccept

  • thenAccept(Consumer action)
  • 任务A执行完执行B,B需要A的结果,但是任务B无返回值

thenApply

  • thenApply(Function fn)

  • 任务A执行完执行B,B需要A的结果,同时任务B有返回值

    (4).CompleteFuture和线程池说明(非常重要)
    

上面的几个方法都有普通版本和后面加Async的版本
以thenRun和thenRunAsync为例,有什么区别?先看结论
1.没有传入自定义线程池,都用默认线程池ForkJoinPool
2.传入了一个自定义线程池如果你执行第一个任务的时候,传入了一个自定义线程池

  • 调用thenRun方法执行第二个任务的时候,则第二个任务和第一个任务是用同一个线程池
  • 调用thenRunAsync执行第二个任务的时候,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池
    3.也有可能处理太快,系统优化切换原则,直接使用main线程处理(把sleep去掉)

(5).对计算速度进行选用

//applyToEither 线程先执行完的输出,输出其中一个
CompletableFuture<String> play1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
            //暂停几秒钟线程
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
            return "play1 ";
        });
        CompletableFuture<String> play2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
            //暂停几秒钟线程
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            return "play2 ";
        });
        CompletableFuture<String> thenCombineResult  = play1.applyToEither(play2, f -> {
            return f + "is winner";
        });
        System.out.println(Thread.currentThread().getName()+"\t"+thenCombineResult.get());

(6).对计算结果进行合并‘

thenCombine 合并

  • 两个CompletionStage任务都完成后,最终能把两个任务的结果一起交给thenCOmbine来处理
  • 先完成的先等着,等待其它分支任务
        CompletableFuture<Integer> completeFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in");
            return 10;
        });
        CompletableFuture<Integer> completeFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in");
            return 20;
        });
        CompletableFuture<Integer> completableFuture = completeFuture1.thenCombine(completeFuture2,
                (x, y) -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in");
            return x + y;
        });
        System.out.println(completableFuture.get()); //输出 30

合并版本

CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in1");
            return 10;
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in2");
            return 20;
        }), (x, y) -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in3");
            return x + y;
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in4");
            return 30;
        }), (x, y) -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "---come in5");
            return x + y;
        });
        System.out.println(completableFuture.get());
        //输出结果
        //ForkJoinPool.commonPool-worker-9	---come in1
		//ForkJoinPool.commonPool-worker-9	---come in2
		//main	---come in3
		//ForkJoinPool.commonPool-worker-2	---come in4
		//main	---come in5
		//60

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

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

相关文章

鸿蒙Hi3861学习七-Huawei LiteOS-M(信号量)

一、简介 信号量&#xff08;Semaphore&#xff09;是一种实现任务间通信的机制&#xff0c;实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中&#xff0c;各任务之间需要同步或互斥实现临界资源的保护&#xff0c;信号量功…

企业官方网站怎么申请?

在数字化时代&#xff0c;企业官方网站是展示企业形象、宣传产品和服务的重要窗口。那么&#xff0c;企业官方网站怎么申请呢&#xff1f;下面是一些简单的步骤。 1、选择合适的网站建设平台 目前市面上有许多网站建设平台&#xff0c;企业需要根据自己的需求和预算选择适合自…

Vue3学习笔记(尚硅谷)

文章目录 一、创建vue3工程1-1、使用vite创建vue3项目1-1、安装开发者工具 二、常用Composition API2-1、setup2-2、ref函数2-3、reactive函数2-4、Vue3的响应式原理2-4-1.Vue2的响应式原理2-4-3.Vue3的响应式原理 2-5、reactive对比ref2-6、setup的两个注意点2-7、计算属性与监…

Excel中创建图表的快捷方式哪些

如果你在Excel中创建了很多图表&#xff0c;你可能正在寻找加快创建和格式化速度的快捷方式。以下是一些可以用于Excel图表的有用快捷方式。 一、在新工作表上创建新图表 要在新工作表上创建新图表&#xff0c;请执行以下操作&#xff1a; ​选择要用于创建图表的数据。按F1…

域适应 Domain adaption(1)

一、定义 1、无监督域自适应 经典机器学习假设训练集和测试集来自相同的分布。 然而&#xff0c;这个假设在现实世界的应用程序中可能并不总是成立&#xff0c;例如&#xff0c;数据来源不同。 这种情况下&#xff0c;域分布之间会存在差异&#xff0c;直接将训练好的模型应…

实时数仓项目开发过程中发现的几个问题和优化点(数据接入)

1、属性值被截断的问题 在数据实时接入阶段&#xff0c;使用NIFI ExecuteScript组件生成增、改、删SQL语句&#xff0c;将SQL语句放到了attribute中(详见视频教程http://mp.weixin.qq.com/s?__bizMzIyNzkwNDE4Nw&mid2247486672&idx1&sn41793a61dc5f7ca6b6f9a34b4…

供应链管理系统软件有哪些?这几款软件很不错

一、供应链管理系统解决什么问题 企业不断引进各类管理理念&#xff0c;落地运用各种信息化系统&#xff0c;然而依旧问题频出&#xff1a; 为什么交付还是常常延期&#xff1f;为什么成本依旧居高不下&#xff1f;为什么质量问题频频发生&#xff1f;为什么库存长期积压&…

SpringBoot启动原理

背景 1> 大家都知道SpringBoot是通过main函数启动的&#xff0c;这里面跟踪代码到处都没有找到while(true)&#xff0c;为什么启动后可以一直跑&#xff1f; 2> SpringBoot默认使用tomcat作为web容器。大家也可以通过在pom文件中exclusion掉tomcat&#xff0c;denpendenc…

113.【Vue-细刷-04】

Vue-03 (二十四)、浏览器存储(WebStorage)1.本地缓存(LocalStorage)(1). 模仿本地缓存-未用JSON转字符串(2).模拟本地缓存-使用JSON转字符串 2.会话缓存(Session Storage)(1).模拟会话缓存(2).会话缓存和本地缓存的区别(3).JSON转换与JSON解析 3.todos案列_本地缓存版(1).mount…

NVIDIA CUDA驱动安装

1 引言 因为笔记本电脑上运行Milvus图像检索代码&#xff0c;需要安装CUDA驱动。电脑显卡型号是NVIDIA GeForce GTX 1050 Ti Mobile, 操作系统是Ubuntu 20.04&#xff0c;内核版本为Linux 5.15.0-72-generic。 2 CUDA驱动测试 参考网上的资料&#xff1a;https://blog.csdn.…

车载测试ADAS-常用场景仿真软件

ADAS&#xff08;Advanced Driber Assistant System&#xff09;&#xff0c;高级驾驶辅助系统&#xff0c;先进驾驶辅 助系统&#xff0c;作用于辅助汽车驾驶&#xff0c;通过感知、决策和执行&#xff0c;帮助驾驶员察觉可能发生的危险&#xff0c;是提高安全性的主动安全技术…

数据结构与算法基础(王卓)(36):交换排序之快排【第三阶段:深挖解决问题】精华!精华!精华!!!重要的事情说三遍

目录 Review&#xff1a; 具体问题&#xff1a; 操作核心&#xff1a; 注&#xff1a; 操作分解&#xff1a; 操作实现&#xff1a; 问题&#xff08;1&#xff09;&#xff1a;进行不一样次数的 if / else 判断 问题&#xff08;2&#xff09;&#xff1a;通过判断条件…

Element-UI

目录 Layout 布局 按钮组件结合 el-icon 使用 单选框 复选框 日期组件 表格 分页 对话框 表单验证 Element-UI是由饿了么前端团队开发的一套基于Vue.js的桌面端组件库&#xff0c;包含了多个常用的UI组件&#xff0c;如按钮、输入框、表格、弹窗等&#xff0c;可以快速…

5.QT应用程序主窗口

本章代码见文末链接 主窗口框架 新建Qt Wisgets项目mymainwindow&#xff0c;类名默认MainWindow&#xff0c;基类默认QMainWindow 更改文字如图&#xff0c;如果中文无法直接输入&#xff0c;可以试试复制粘贴 “动作编辑器”中&#xff08;默认在右下角&#xff09;&…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

2023鲁大师评测沟通会:鲁大师尊享版登场、“鲁小车”正式上线

作为硬件评测界的“老兵”&#xff0c;鲁大师不仅有着十几年的硬件评测经验&#xff0c;并且一直都在不断地尝试、不断地推陈出新。在5月9日举行的“2023年鲁大师评测沟通会”上&#xff0c;鲁大师向大众展示了在过去一年间取得的成果。 PC业务迭代升级&#xff0c;鲁大师客户端…

day(2,3)-内核模块

内核模块上 主要内容 向内核添加新功能 内核模块基础代码讲解 内核模块多源文件编程 内核模块信息宏 一、向内核添加新功能 1.1 静态加载法&#xff1a; 即新功能源码与内核其它代码一起编译进uImage文件内 Kconfig是make menuconfig的界面配置文件 1.2动态加载法&am…

Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本

目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结

2022-4-4基于单片机的MQ2烟雾传感器报警系统设计

基于单片机的MQ2烟雾传感器报警系统设计 源代码和仿真图及MQ2相关资料可在文末的链接中下载 该系统实现的功能&#xff1a;检测空气中的烟雾浓度并实现超阈值报警 该系统组成由&#xff1a; 单片机最小系统、ADC0832、MQ2烟雾传感器、LCD1602液晶模块、声光报警模块、按键模…

研究人员发现微软Azure API管理服务存在3个漏洞

微软Azure API管理服务中披露了三个新的安全漏洞&#xff0c;恶意行为者可能会滥用这些漏洞来访问敏感信息或后端服务。 据以色列云安全公司Ermetic称&#xff0c;这包括两个服务器端请求伪造(SSRF)漏洞和API管理开发人员门户中的一个不受限制的文件上传功能实例。 安全研究员…