【JUC】十二、CompletableFuture(上)

news2024/9/23 17:13:56

文章目录

  • 1、CompletionStage
  • 2、创建CompletableFuture对象
  • 3、CompletbaleFuture
  • 4、函数式接口
  • 5、chain链式调用
  • 6、实例:电商网站比价

针对前面提到的Future接口的实现类FutureTask的缺点,考虑传入一个回调函数,当任务完成时,自动去调用,since Java8,有了Future接口的新实现CompleteableFuture,CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

在这里插入图片描述

1、CompletionStage

CompletionStage是CompletableFuture实现的接口,代表异步计算过程中的某一个阶段,一个阶段完成后,可能触发另一个阶段。类比Linux的管道符。

stage.thenApply(x -> square(x))
   	 .thenAccept(x -> System.out.println(x))
   	 .thenRun(() -> System.out.println())

一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发。

2、创建CompletableFuture对象

CompletableFuture是Future接口新的实现类,是对实现异步任务的的再一次扩展。创建CompletableFuture对象:

CompletbaleFuture future = new CompletableFuture();

在这里插入图片描述

可以看到这样创建的是一个未完成的CompletableFuture对象,即这个构造方法只是语法层面提供一下,真正创建CompletableFuture对象并不用它。正确用法为调用:

runAsync
supplyAsync

在这里插入图片描述

无返回值的,传参为:

  • Runnable对象
  • Runnable对象+线程池

有返回值的:

  • Supplier(供给型函数式接口)
  • Supplier+线程池

在这里插入图片描述

调用runAsync和supplyAsync这两个静态方法时,关于Executor参数:

  • 没有指定Executor,使用默认的ForkJoinPool.commonPool()做为线程池来执行异步任务
  • 指定了Executor,则使用指定的线程池

前面提到的Future(FutureTask)+ 线程池来提高执行效率,这里CompletableFuture也可以结合线程池。

public class CompletDemo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
            
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        System.out.println(completableFuture.get());
    }
}

在这里插入图片描述

指定线程池后,线程名称发生变化:

public class CompletDemo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
        Executor pool = Executors.newFixedThreadPool(3);
        
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },pool);
        
        System.out.println(completableFuture.get());
    }
}

在这里插入图片描述

CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
	    System.out.println(Thread.currentThread().getName());
	    try {
	        TimeUnit.SECONDS.sleep(1);
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	    return "9527";
	});
System.out.println(completableFuture.get());

在这里插入图片描述

public class CompletDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
        Executor pool = Executors.newFixedThreadPool(3);
        
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "9527";
        },pool);
        
        System.out.println(completableFuture.get());
    }
}

3、CompletbaleFuture

从Java8开始引入CompletbaleFuture,它是Future功能的增强版,会较少阻塞和轮询,可以传入回调函数,当异步任务完成或者发生异常时,自动调用传入的回调方法。

在这里插入图片描述

上面创建CompletableFuture对象时,演示的CompletableFuture和FutureTask差不多,这里演示CompletableFuture的回调:

  • whenComplete:传入一个BiConsumer,即异步任务完成后自己调用的逻辑
  • exceptionally:传入一个Function,即异步任务发生异常时的逻辑
public class CompletDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(3);

        try {
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "come in...");
                //十以内的随机数
                int result = ThreadLocalRandom.current().nextInt(10);
                if(result > 5){
                    throw new RuntimeException("test");
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("----1s后该任务出结果:" + result);
                return result;
            },pool).whenComplete((v,e) -> {   //v是上一步的计算结果,e为上一步发生了的异常
                if (null == e){
                    System.out.println("计算完成,update:" + v);
                }
            }).exceptionally(e -> {
                e.printStackTrace();
                System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());
                return null;
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
        System.out.println(Thread.currentThread().getName() + "线程接着去忙其他任务了");
        //使用默认线程池时,主线程结束会导致线程池立刻关闭,导致分支线程task没来得及输出
        //可使用自定义线程池或者sleep,不要让主线程立刻结束


    }
}

注意:

  • supplyAsync或者runAsync方法不传pool时,则默认使用fork-join-pool线程池,此时主线程结束会导致线程池立刻关闭,可能会导致分支线程task没来得及输出
  • 可使用自定义线程池或者让主线程sleep一会儿,总之不要让主线程立刻结束

别程序运行,分支任务结果没输出,一脸懵不知道啥原因。

在这里插入图片描述

生产随机数:

//十以内的随机数
int result = ThreadLocalRandom.current().nextInt(10);

get和join的区别:

二者作用基本一样,join在编译阶段不会报异常,不用throw或者try处理

4、函数式接口

以下是后面要用到的几个函数式接口(since Java8):
在这里插入图片描述
在这里插入图片描述
消费型函数式接口:

在这里插入图片描述

两个入参的消费型函数式接口:

在这里插入图片描述
生产型函数式接口:

在这里插入图片描述

总结:

在这里插入图片描述

5、chain链式调用

链式编程一直在用,这里记下@Accessors(之前有个@Builder)

@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class Student {

    private String uid;

    private String name;

    private String address;
}

加@Accessors注解后,再给对象赋值,就可以直接链式调用。

new Student().setUid("001").setName("code9527").setAddress("Tianjin");

6、实例:电商网站比价

需求分析:

对比同一款商品在各大电商平台的售价,返回一个List,元素格式:

-MySQL》 in JD price is 88.05
-Mysql》 in TianMao price is 90.43
- ...

定义个实体类:

@AllArgsConstructor
public class NetMall {

    @Getter
    private String netMallName;   //电商平台名称

    /**
     * 模拟计算某平台某商品的价格
     * @param productName 商品名称
     * @return 商品价格
     */
    public double calcPrice(String productName){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //自定义一个价格计算方式,模拟查到返回一个价格
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }
}

实现这个功能,可step by step,即一个一个电商平台去查:

public class CompletablePlatPrice {

    static List<String> platList = Arrays.asList(
            "JD",
            "TaoBao",
            "TianMao",
            "PDD"
    );

    public static List<String> getPrice(String productName){
        return platList.stream()
                .map(t -> String.format(productName + " in %s price is %.2f", t, new NetMall(t).calcPrice(productName)))
                .collect(Collectors.toList());
    }
    public static void main(String[] args) {

        long startTime = System.currentTimeMillis();
        List<String> priceList = getPrice("mysql基础");
        for (String price : priceList) {
            System.out.println(price);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("--耗时--" + (endTime-startTime) + "ms");
    }
}

String.format方法修改字符串格式,p1为pattern,p2为占位符对应的变量 %.2f即小数点后保留两位。这个功能,使用异步task来实现,即多个CompletableFuture查多个平台:

public static List<String> getPriceByCompletableFuture(String productName){
    return platList.stream()
            .map(t -> CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f", t, new NetMall(t).calcPrice(productName))))
            .collect(Collectors.toList())
            .stream()
            .map(t -> t.join())
            .collect(Collectors.toList());
}

此后,电商平台数量再增加,新实现的耗时也基本不变,这就是从功能到性能:

public static void main(String[] args) {

    long startTime = System.currentTimeMillis();
    List<String> priceList = getPrice("mysql基础");
    for (String price : priceList) {
        System.out.println(price);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("--耗时--" + (endTime-startTime) + "ms");

    long startTime2 = System.currentTimeMillis();
    List<String> priceList2 = getPriceByCompletableFuture("mysql基础");
    for (String price : priceList2) {
        System.out.println(price);
    }
    long endTime2 = System.currentTimeMillis();
    System.out.println("--耗时--" + (endTime2-startTime2) + "ms");
}

在这里插入图片描述

使用IDEA看下上面的过程,实际是把一个List<String>里的String转成了一个个CompletableFuture对象,得到List<CompletableFuture<Result>>,再二次stream处理,join或者get拿到异步计算的值。

在这里插入图片描述

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

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

相关文章

大数据存储技术期中考点梳理

1.CAP理论 分布式系统的CAP理论: 首先将分布式系统中的三个特性进行如下归纳: 口(一致性(C):在分布式系统中的所有数据备份&#xff0c;在同一时刻是否有同样的值。(等于所有节点访问同一份最新的数据副本) 口可用性(A):在集群中一部分节点故障后&#xff0c;集群整体是否还能…

Ant System: An Autocatalytic Optimizing Process-Ant 系统:自动催化优化过程

文章目录 标题摘要关键字结论研究背景1. Introduction 常用基础理论知识2. The Ant system 研究内容、成果3. The Ant-density and Ant-quantity algorithms4. The Ant-cycle algorithm5. Computational results5.1 Parameters setting5.2 Number of ants5.3 Which town should…

慢 SQL 分析及优化

目录 分析慢 SQL SQL 优化 单表优化 多表优化 慢 SQL&#xff1a;指 MySQL 中执行比较慢的 SQL排查慢 SQL 最常用的方法&#xff1a;通过慢查询日志来查找慢 SQL MySQL 的慢查询日志是 MySQL 提供的一种日志记录&#xff0c;它用来记录在 MySQL 中响应时间超过阈值的语句&…

从零开始的c语言日记day37——数组指针练习

一、 取地址数组储存在了*p里&#xff0c;里面储存的是整个数组的地址但本质也是第一个元素的地址解引用后1为4个字节所以就可以打印数组了。但一般不用这种方法 这样更方便一些 打印多维数组 如果不用这样传参&#xff0c;用指针传参怎么做呢&#xff1f; Main里函数的arr表示…

配置 Mantis 在 Windows 上的步骤

配置 Mantis Bug Tracker 在 Windows 上的步骤 Mantis Bug Tracker 是一款开源的缺陷跟踪系统&#xff0c;用于管理软件开发中的问题和缺陷。在 Windows 环境下配置 Mantis 可以帮助开发者更方便地进行项目管理。以下是一个详细的教程&#xff0c;包含了 EasyPHP Devserver 和…

多线程(进程池代码)

线程池介绍 那究竟什么是线程池呢&#xff1f; 线程池是一种线程使用模式. 线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能. 而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务. 这避免了在处理短时间任务时创建与销毁线程的代价. 线…

2023年【A特种设备相关管理(锅炉压力容器压力管道)】新版试题及A特种设备相关管理(锅炉压力容器压力管道)模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【A特种设备相关管理&#xff08;锅炉压力容器压力管道&#xff09;】新版试题及A特种设备相关管理&#xff08;锅炉压力容器压力管道&#xff09;模拟考试题库&#xff0c;包含A特种设备相关管理&#xff08;锅…

记录:Unity脚本的编写8.0

目录 需求分析设计GUI包含账号和密码输入栏&#xff0c;包括登录和注册按键添加背景音乐编写脚本控制音乐 退出按钮编写脚本 背景图片完整代码 一个小demo&#xff0c;登录和注册的实现&#xff08;包括GUI和数据库操控&#xff09; 需求分析 自行设计GUI&#xff0c;要求 1.包…

手机上使用的备忘录怎么分享给别人看?

手机备忘录大家应该都不陌生&#xff0c;通常大家使用手机备忘录会整理记录一些容易忘记的事情&#xff0c;多数手机备忘录被用来罗列重要的备忘事项&#xff0c;以防止自己遗忘&#xff0c;有时候大家也喜欢分享一些手机备忘录&#xff0c;但是并不是所有的手机备忘录都支持分…

Spring Boot 3.2.0 虚拟线程初体验 (部分装配解析)

写在前面 spring boot 3 已经提供了对虚拟线程的支持。 虚拟线程和平台线程主要区别在于&#xff0c;虚拟线程在运行周期内不依赖操作系统线程&#xff1a;它们与硬件脱钩&#xff0c;因此被称为 “虚拟”。这种解耦是由 JVM 提供的抽象层赋予的。 虚拟线程的运行成本远低于平…

Android Bitmap保存成至手机图片文件,Kotlin

Android Bitmap保存成至手机图片文件&#xff0c;Kotlin fun saveBitmap(name: String?, bm: Bitmap) {val savePath Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()if (!Files.exists(Paths.get(savePath))) {Log.d("保存文…

Linux以nohup方式运行jar包

1、在需要运行的jar包同级目录下建立启动脚本文件&#xff1a; 文件内容&#xff1a; #! /bin/bash #注意&#xff1a;必须有&让其后台执行&#xff0c;否则没有pid生成 jar包路径为绝对路径 nohup java -jar /usr/local/testDemo/jdkDemo-0.0.1-SNAPSHOT.jar >/us…

计算机基础知识60

MySQL分组 # 概念&#xff1a;分组是按照某个指定的条件将单个单个的个体分成一个个整体 # MySQL分组的关键字&#xff1a;group by # 分组一般配合聚合函数使用&#xff1a; sum max min avg count 基本的语法格式: group by 字段名 [having 条件表达式] # 单独使用 group by关…

机器学习与低代码:简化AI开发的未来

机器学习&#xff08;Machine Learning&#xff09;的应用如火如荼地扩展&#xff0c;其影响力和潜力在各行业得到了充分展现。然而&#xff0c;对于广大开发者和企业来说&#xff0c;机器学习模型的构建和部署并非易事&#xff0c;其中涉及的复杂过程和专业知识往往令人望而却…

苍穹外卖项目笔记(6)— Redis操作营业状态设置

1 在 Java 中操作 Redis 1.1 Redis 的 Java 客户端 Jedis&#xff08;官方推荐&#xff0c;且命令语句同 redis 命令&#xff09;Lettuce&#xff08;底层基于 Netty 多线程框架实现&#xff0c;性能高效&#xff09;Spring Data Redis&#xff08;对 Jedis 和 Lettuce 进行了…

深入理解强化学习——马尔可夫决策过程:备份图(Backup Diagram)

分类目录&#xff1a;《深入理解强化学习》总目录 在本文中&#xff0c;我们将介绍备份&#xff08;Backup&#xff09;的概念。备份类似于自举之间的迭代关系&#xff0c;对于某一个状态&#xff0c;它的当前价值是与它的未来价值线性相关的。 我们将与下图类似的图称为备份图…

HassOS使用nmcli设置静态IPv4地址及网关、DNS

目录 显示hass在使用的默认连接显示此连接的所有配置编辑hass默认连接添加静态IP地址添加DNS和网关删除DNS查看IPv4属性保存配置并退出nmcli重载配置 首先控制台登陆Home Assistant OS Welcome to Home Assistant homeassistant login:使用root用户登录&#xff08;无需密码&a…

Linux基础项目开发1:量产工具——显示系统(二)

前言&#xff1a; 前面我们已经对这个项目的基本框架有了一个初步的了解与认识&#xff0c;要实现显示管理器与输入管理器&#xff0c;有输入有输出基本就实现这个项目的大部分功能了&#xff0c;首先我们先来做显示系统&#xff0c;对于上层系统为了让程序更好扩展&#xff0c…

MySQL--日志

日志 错误日志 错误日志是MySQL中最重要的日志之一&#xff0c;它记录了当mysqld启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息 当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&#xff0c;默认…

Mysql 高级日志binlog、undoLog、redoLog 详解

数据更新流程与日志记录&#xff1a; undoLog&#xff1a; binLog&#xff1a; redoLog&#xff1a;