Java8新特性 CompletableFuture

news2024/11/15 21:38:19

Java8新特性 CompletableFuture

什么是CompletableFuture?

CompletableFuture类的设计灵感来自于 Google Guava 的 ListenableFuture 类,它实现了 FutureCompletionStage 接口并且新增了许多方法,它支持 lambda表达式,通过回调利用非阻塞方法,提升了异步编程模型。它允许我们通过在与主应用程序线程不同的线程上(也就是异步)运行任务,并向主线程通知任务的进度、完成或失败,来编写非阻塞代码。

在这里插入图片描述

为什么引入CompletableFuture?

Java 的 1.5 版本引入了 Future,你可以把它简单的理解为运算结果的占位符,它提供了两个方法来获取运算结果。

  • get():调用该方法线程将会无限期等待运算结果。
  • get(long timeout, TimeUnit unit):调用该方法线程将仅在指定时间 timeout 内等待结果,如果等待超时就会抛出 TimeoutException 异常。

Future 可以使用 RunnableCallable 实例来完成提交的任务,通过其源码可以看出,它存在如下几个问题:

  • 阻塞 调用 get() 方法会一直阻塞,直到等待直到计算完成,它没有提供任何方法可以在完成时通知,同时也不具有附加回调函数的功能。
  • 链式调用和结果聚合处理 在很多时候我们想链接多个 Future 来完成耗时较长的计算,此时需要合并结果并将结果发送到另一个任务中,该接口很难完成这种处理。
  • 异常处理 Future 没有提供任何异常处理的方式。

而我们的CompletableFuture则成功地解决了上述的这些问题,下面将一一介绍CompletableFuture的一些常用的API方法的使用:

CompletableFuture的API

首先CompletableFuture实现Future接口,故Future接口存在的常见方法它本身也存在,这里不再进行讲解,处理以外,它还提供了手动完成complete()方法,判断是否完成isDon()方法,取消执行cancel()等等。

1. 异步执行方法 runAsync/supplyAsync

runAsync接受的参数为Runnable参数,无返回值;而supplyAsync接受的参数则为Supplier supplier,即有返回值的函数式接口,可以参考下表回顾一下函数式接口:

在这里插入图片描述

故我们简单编写一个方法,分别使用这两种异步任务的调用方式:

public class TestCompletableFuture {
    public static void main(String[] args) {
      	//runAysnc
        testRunAsync();
      	//supplyAsync
        testSupplyAsync();
    }
    
    /**
     * 异步执行Code 无返回值
     */
    private static void testRunAsync() {
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("runAsync...");
        });
    }

    /**
     * 异步执行Code 有返回值
     */
    private static void testSupplyAsync() {
        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "supplyAsync...";
        });
        try {
            //异步获取结果
            String result = stringCompletableFuture.get();
            System.out.println("result:" + result);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
}

输出结果:

runAsync...
result:supplyAsync...

2. 完成执行 whenComplete/whenCompleteAsync

当我们定义上面的runAsync或者supplyAsync方法执行完毕以后,可以调用whenComplete或者whenCompleteAsync继续执行任务,区别在于:

  • whenComplete是有之前执行异步任务的线程继续执行任务,如果定义多个whenComplete,则它们之间是链式串行调用的关系
  • whenCompleteAsync则是将任务提交到线程池中进行执行,如果定义多个whenCompleteAsync,则它们之间则是并行调用的关系

首先我们来看whenComplete的使用方法:

main() {
  testWhenComplete();
}
private static void testWhenComplete() {
  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello World");
  //定义两个whenComplete任务,此时会先执行01,再执行02
  future.whenComplete((res, error) -> {
    try {
      Thread.sleep(5000);
      System.out.println("whenComplete01");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  future.whenComplete((res, error) -> {
    try {
      Thread.sleep(1000);
      System.out.println("whenComplete02");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  System.out.println("completed...");
}

输出结果:

//sleep 5s
whenComplete01
//sleep 1s  
whenComplete02
completed...

然后我们再来看whenCompleteAsync的使用方法:

main() {
  testWhenCompleteAsync();
}

private static void testWhenCompleteAsync() {
  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello World");
  //此时定义两个whenCompleteAsync,在future执行完毕以后,两者并发执行
  future.whenCompleteAsync((res, error) -> {
    try {
      Thread.sleep(5000);
      System.out.println("whenComplete01");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  
  future.whenCompleteAsync((res, error) -> {
    try {
      Thread.sleep(1000);
      System.out.println("whenComplete02");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  
  System.out.println("completed...");
  try {
    Thread.sleep(10000);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

输出结果:

completed...
//sleep 1s  
whenComplete02
//sleep 4s  
whenComplete01

3. 链式执行任务 thenCompose/thenApply

两者均可将CompletableFuture连接起来,但是存在一些差异:

  • thenApply()接收的是前一个调用返回的结果,然后对该结果进行处理。
  • thenCompose()接收的是前一个调用的stage,返回flat之后的的CompletableFuture,即将上一个Future的执行结果作为下一个Future的输入

thenApply方法

main() {
  testThenApply();
}
private static void testThenApply() {
  CompletableFuture<String> completableFuture = CompletableFuture
    .supplyAsync(() -> "Hello")
    //s为上个任务的结果,可对其进行处理
    .thenApply(s -> s + "World");
  try {
    String result = completableFuture.get();
    System.out.println(result);
  } catch (InterruptedException e) {
    e.printStackTrace();
  } catch (ExecutionException e) {
    e.printStackTrace();
  }
}

输出结果:

HelloWorld

thenCompose方法

main(){
  testThenCompose();
}
private static void testThenCompose() {
  CompletableFuture<String> stringCompletableFuture = CompletableFuture
    .supplyAsync(() -> "Hello")
    //s即为上一个CompletableFuture的输出结果
    .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "World"));
  try {
    String result = stringCompletableFuture.get();
    System.out.println(result);
  } catch (InterruptedException e) {
    e.printStackTrace();
  } catch (ExecutionException e) {
    e.printStackTrace();
  }
}

输出结果:

HelloWorld

4. 合并处理 thenCombine/thenAcceptBoth

两个合并的方法本质上的区别在于thenCombine有返回值,而thenAcceptBoth无返回值

public class TestCompletableFuture {
    public static void main(String[] args) {
        //有返回值的合并操作
        testThenCombine();
        //无返回值的合并操作
        testThenAcceptBoth();
    }

    private static void testThenAcceptBoth() {
        CompletableFuture.supplyAsync(() -> "Hello")
                .thenAcceptBoth(
                    CompletableFuture.supplyAsync(() -> "World"), (s1, s2) -> System.out.println(s1 + s2)
        );
    }

    private static void testThenCombine() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello")
                .thenCombine(
                        CompletableFuture.supplyAsync(() -> "World"), (c1, c2) -> c1 + c2
                );
        try {
            String result = completableFuture.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

HelloWorld
HelloWorld

5. 聚合处理 allOf/anyOf

我们需要并行执行任务时,通常我们需要等待所有的任务都执行完毕再去处理其他的任务,那么我们可以用到allOf,等同于 CountDownLatch闭锁。而anyOf则是任务中有一个完成则直接去处理其他的任务,无需再等待其他任务执行完毕。

allOf方法:

public class TestCompletableFuture {
    public static void main(String[] args) {
        testAllOf();
    }

    private static void testAllOf() {
        //定义三个任务
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> System.out.println("task1 running"));
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> System.out.println("task2 running"));
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() ->
        {
            try {
                //模拟延迟任务
                Thread.sleep(5000);
                System.out.println("task3 running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //合并三个任务
        CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2, future3);
        System.out.println("task waiting...");
        //等待所有任务执行完毕
        future.join();
        System.out.println("task completed");
    }
}

输出结果:

task1 running
task2 running
task waiting...
//waiting about 5s
task3 running
task completed

anyOf方法:

public class TestCompletableFuture {
    public static void main(String[] args) {
        testAnyOf();
    }

    private static void testAnyOf() {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(
                () -> {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("task1 running...");
                }
        );
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(
                () -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("task2 running...");
                }
        );
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(
                () -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("task3 running...");
                }
        );
        //只要有一个任务执行完毕,即不再等待...
        CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(future1, future2, future3);
        System.out.println("task waiting...");
        completableFuture.join();
        System.out.println("task completed");
    }
}

输出结果:任务2完毕继续其他任务,不再等待任务1和任务3

task waiting...
task2 running...
task completed

6. 多任务返回值联合处理 join

public class TestCompletableFuture {
    public static void main(String[] args) {
        testJoin();
    }

    private static void testJoin() {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Future");
        //对三个任务的返回值进行join处理
        String result = Stream.of(future1, future2, future3)
                .map(CompletableFuture::join)
                .collect(Collectors.joining(" "));
        System.out.println(result);
    }
}

输出结果:

Hello World Future

7. 异常处理 handle(result, exception)

我们在异步任务过程中可以抛出异常,并通过handle进行异常处理:

public class TestCompletableFuture {
    public static void main(String[] args) {
        testHandle();
    }

    /**
     * 异常处理
     */
    private static void testHandle() {
        //模拟参数为null
        String param = null;
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            if (param == null) {
                throw new RuntimeException("params_error");
            }
            return "Hello" + param;
        }).handle((res, ex) -> res != null ? res : ex.getMessage());

        try {
            String result = completableFuture.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

以上就是CompletableFuture的常见的一些API,当然还有很多其他一些API方法,这里不再赘述,可以查看对应的API文档进行使用…

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

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

相关文章

【IDEA插件】这5款IDEA插件,堪称代码BUG检查神器!

随着业务的发展&#xff0c;系统会越来越庞大&#xff0c;原本简单稳定的功能&#xff0c;可能在不断迭代后复杂度上升&#xff0c;潜在的风险也随之暴露&#xff0c;导致最终服务不稳定&#xff0c;造成业务价值的损失。而为了减少这种情况&#xff0c;其中一种比较好的方式就…

5.盒子阴影(重点)

提示&#xff1a;css3中新增了盒子阴影&#xff0c;我们可以使用box-shadow属性为盒子添加阴影。 1、语法&#xff1a; div{ box-shadow:"h-shadow"或者“v-shadow” } 解释&#xff1a; h-shadow 必须&#xff0c;水平阴影位置&#xff0c;允许负值。 v-shado…

UE4 回合游戏项目 18- 退出战斗

在上一篇&#xff08;UE4 回合游戏项目 17- 进入指定区域触发战斗事件&#xff09;基础上完成击败敌人从而退出战斗的功能。 效果&#xff1a; 步骤&#xff1a; 1.打开“battleScenario”蓝图&#xff0c;添加一个自定义事件&#xff0c;命名为“离开战斗” ​ 2.删除所有…

[附源码]Python计算机毕业设计_社区无接触快递栈

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

手撕二叉搜索树

目录 一、概念 二、常见操作 2.1 查找操作 2.2 插入操作 2.3 删除操作 三、模型应用 3.1 K模型 3.2 KV模型 3.3 代码完整实现 四、 性能分析 一、概念 二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树 它或者是一棵空树&#xff0c;或者是具有以下…

Spring整合Mybatis和Junit小案例(9)

Spring整合Mybatis和Junit环境准备步骤1&#xff1a;准备数据库步骤2&#xff1a;创建项目导入jar包步骤3&#xff1a;根据数据库的表创建模型类步骤4&#xff1a;创建Dao接口步骤5&#xff1a;创建Service接口和实现类步骤6&#xff1a;添加jdbc.properties文件步骤7&#xff…

5种常用格式的数据输出,手把手教你用Pandas实现

导读:任何原始格式的数据载入DataFrame后,都可以使用类似DataFrame.to_csv()的方法输出到相应格式的文件或者目标系统里。本文将介绍一些常用的数据输出目标格式。 01 CSV DataFrame.to_csv方法可以将DataFrame导出为CSV格式的文件,需要传入一个CSV文件名。 df.to_csv(done.…

在 SPRING Boot JPA 中调用带有本机查询中的参数的存储过程

配置pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.…

惊了!10万字的Spark全文!

Hello&#xff0c;大家好&#xff0c;这里是857技术社区&#xff0c;我是社区创始人之一&#xff0c;以后会持续给大家更新大数据各组件的合集内容&#xff0c;路过给个关注吧!!! 今天给大家分享一篇小白易读懂的 Spark万字概念长文&#xff0c;本篇文章追求的是力求精简、通俗…

Linux(基于Centos7)(一)

文章目录一、任务介绍二、基本操作命令三、目录操作命令四、文件操作命令五、查看系统信息六、其他常用命令一、任务介绍 Linux服务器配置与管理&#xff08;基于Centos7.2&#xff09;任务目标&#xff08;一&#xff09; 实施该工单的任务目标如下&#xff1a; 知识目标 1、…

RNA剪接增强免疫检查点抑制疗效

什么是 RNA 剪接&#xff1f;真核生物基因包含一系列外显子和内含子&#xff0c;内含子必须在转录过程中被移除以便成熟的 mRNA 被翻译成蛋白质&#xff0c;RNA 剪接则是这一过程中至关重要的一步。RNA 剪接包含两类剪接事件。组成型剪接 (constitutive splicing): RNA 剪接的一…

【蓝桥杯Web】第十四届蓝桥杯(Web 应用开发)模拟赛 1 期-职业院校组 | 精品题解

&#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f5a5;️ Nodejs专栏&#xff1a;Node.js从入门到精通 &#x1f5a5;️ TS知识总结&#xff1a;十万字TS知识点总结 &#x1f449; 你的一键三连是我更新的最大动力❤️&#xff0…

企业级Java EE架构设计精深实践

内容简介 本书全面、深入介绍了企业级Java EE设计的相关内容&#xff0c;内容涵盖了Java EE架构设计的常见问题。 本书每一章讲解一个Java EE领域的具体问题&#xff0c;采用问题背景、需求分析、解决思路、架构设计、实践示例和章节总结的顺序组织内容&#xff0c;旨在通过分…

生成树(STP)

1.详细说明STP的工作原理 在二层交换网络中&#xff0c;逻辑的阻塞部分的接口&#xff0c;实现从跟交换机到所有节点唯一的路径称为最佳路径&#xff0c;生成一个没有环路的拓扑。当最佳路径出现故障时&#xff0c;个别被阻塞的接口将打开&#xff0c;形成备份链路。 2. STP的…

Redis的发布和订阅

Redis的发布和订阅 什么是发布和订阅 redis发布订阅&#xff08;pub/sub&#xff09;是一种消息通信模式&#xff1a;发布者&#xff08;pub&#xff09;发布消息&#xff0c;订阅者&#xff08;sub&#xff09;接收消息。 redis客户端可以订阅任意数量的频道。 redis的发布…

vue3【计算属性与监听-详】

一、计算属性--简写形式 需求&#xff1a;通过计算属性&#xff0c;计算一个人的全名。 <template><h1>基本信息</h1>姓&#xff1a;<input type"text" v-model"personInfo.firstName"><hr>名&#xff1a;<input type&…

综合实验高级网络—— 配置三层 热备等网络技术

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。个人爱好: 编程&#xff0c;打篮球&#xff0c;计算机知识个人名言&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石…

结合邻域连接法的蚁群优化(NACO)求解TSP问题(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

傅里叶级数与傅里叶变换

一、Games101 中出现的傅里叶变换(FT)的简单推导 到底什么是傅里叶变换&#xff1a;它的物理意义是什么&#xff0c;公式又从何而来&#xff1f; 以下的内容出现在 Games101 中的第八章&#xff1a;光栅化&#xff08;深度测试与抗锯齿&#xff09; 中&#xff0c;课程中这一部…

OpenAI Whisper论文笔记

OpenAI Whisper论文笔记 OpenAI 收集了 68 万小时的有标签的语音数据&#xff0c;通过多任务、多语言的方式训练了一个 seq2seq &#xff08;语音到文本&#xff09;的 Transformer 模型&#xff0c;自动语音识别&#xff08;ASR&#xff09;能力达到商用水准。本文为李沐老师…