首页 > 编程语言 >Java | 一分钟掌握异步编程 | 5 - CompletableFuture异步

Java | 一分钟掌握异步编程 | 5 - CompletableFuture异步

时间:2023-04-07 17:33:33浏览次数:40  
标签:异步 Java System 任务 CompletableFuture println 执行 out

 作者:Mars酱

 声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。

 转载:欢迎转载,转载前先请联系我!

前言

继续讲,前面讲了Future实现异步,优点缺点也都有,这里讲使用CompletableFuture机制,目前为止,应该说JDK原生提供的异步方式的最优方案就是CompletableFuture了,已知的开源框架Dubbo中的RPC协议实现、常用的注册配置中心Nacos中CP协议的具体实现JRaft模块,也使用CompletableFuture。好了,名气大,不多说了。

CompletableFuture实现异步

我们还是用 Java | 一分钟掌握异步编程 | 4 - Future异步 - 掘金 (juejin.cn) 中3号桌点餐的例子,3号桌点了很多餐:红烧鱼、油焖虾,中途厨子还要上厕所,这个过程,我们用 CompletableFuture 来实现,我们把两道菜拆分成两个任务,一个内急的好厨子一定是可以双飞两道菜的,先飞一道鱼:

/**
 * @author mars酱
 */
public class BraisedFish {
    public BraisedFish() {
        System.out.println("1. 抓鱼");
    }

    public void kill() {
        System.out.println("2. 杀鱼");
    }

    public void cooking() {
        System.out.println("3. 红烧鱼制作中...");
    }
}

再来一盘虾:

/**
 * @author mars酱
 */
public class BraisedPrawns {
    public BraisedPrawns() {
        System.out.println("1. 捉虾");
    }

    public void kill() {
        System.out.println("2. 杀虾");
    }

    public void cooking() {
        System.out.println("3. 油焖大虾制作中...");
    }
}

厨师的动作:

/**
 * @author mars酱
 */
public class Chef {
    public Chef() {
        System.out.println("1. 米其林大厨师");
    }

    public void toilet() {
        System.out.println("厨师正在拉,请稍后...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

做鱼的任务顺序必须是捉鱼 -> 杀鱼 -> 红烧;做虾的顺序必须是先捉后杀再油焖。最后,整体流程是先做鱼,再做虾,厨子上厕所,菜好了等着厨子上完厕所洗完手,再撒上葱花:

/**
 * @author mars酱
 */
public class NoodlesRestaurant3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. 厨师、鱼、虾都准备到位
        Chef chef = new Chef();
        BraisedFish braisedFish = new BraisedFish();
        BraisedPrawns braisedPrawns = new BraisedPrawns();
        System.out.println("---- 准备工作完成 ----");

        // 2. 做鱼
        CompletableFuture<String> killFishTask = CompletableFuture.supplyAsync(() -> {
            braisedFish.kill();
            return ">> 鱼杀好了,准备红烧";
        });
        CompletableFuture<String> cookingFishTask = killFishTask.thenApply((result) -> {
            System.out.println(result);
            braisedFish.cooking();
            return ">> 红烧鱼作品已完成";
        });

        // 3. 做虾
        CompletableFuture<String> killPrawnsTask = CompletableFuture.supplyAsync(() -> {
            braisedPrawns.kill();
            return ">> 虾杀好了,准备油焖";
        });
        CompletableFuture<String> cookingPrawnsTask = killPrawnsTask.thenApply((result) -> {
            System.out.println(result);
            braisedPrawns.cooking();
            return ">> 油焖虾作品已完成";
        });

        // 4. 厨师拉粑粑
        CompletableFuture<String> chefToilet = CompletableFuture.supplyAsync(() -> {
            chef.toilet();
            return ">> 厨师的粑粑拉完啦!";
        });
        CompletableFuture<String> chefWashHands = chefToilet.thenApply((result) -> {
            System.out.println(result);
            return ">> 厨师洗手!";
        });

        // 5. 菜品都完成了
        CompletableFuture<String> foodCompleted = cookingFishTask.thenCombineAsync(cookingPrawnsTask, (w, s) -> w + " 和 " + s + ", 等待厨师撒葱花!");

        // 6. 等待厨师上完厕所撒葱花
        CompletableFuture<String> serving = chefWashHands.thenApply((result) -> {
            String join = foodCompleted.join();
            System.out.println(join);
            System.out.println(result);
            System.out.println(">> 厨师用擦过花的手撒葱花,干净又卫生!");
            return ">> 上菜吧~";
        });
        System.out.println(serving.get());

    }
}

运行一下下:

Java | 一分钟掌握异步编程 | 5 - CompletableFuture异步_Java

很好,厨师果然正在拉,继续等待...

Java | 一分钟掌握异步编程 | 5 - CompletableFuture异步_Completable_02

方法简介

以上用到了一些CompletableFutre的api,这里把常用的api简单介绍一下:

一般

supplyAsync:执行CompletableFuture任务,支持返回值

runAsync:执行CompletableFuture任务,没有返回值。

异步回调

thenRun/thenRunAsync:做完第一个任务后,再做第二个任务。

thenAccept/thenAcceptAsync:第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,但是回调方法是没有返回值的

thenApply:第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。

exceptionally:某个任务执行异常时,执行的回调方法;并且有抛出异常作为参数,传递到回调方法。

whenComplete:某个任务执行完成后,执行的回调方法,无返回值;并且whenComplete方法返回的CompletableFuture的result是上个任务的结果。

handle:某个任务执行完成后,执行回调方法,并且是有返回值的;并且handle方法返回的CompletableFuture的result是回调方法执行的结果。

thenCombineAsync:两个异步任务t1、t2是并行执行,彼此无先后依赖顺序,thenCombineAsync适合将两个并行执行的异步任务的结果合并返回成一个新的future。

都完成

thenCombine / thenAcceptBoth / runAfterBoth都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个任务

区别在于:

  • thenCombine:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
  • thenAcceptBoth: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
  • runAfterBoth 不会把执行结果当做方法入参,且没有返回值。

其中一个完成

applyToEither / acceptEither / runAfterEither 都表示:将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个任务。区别在于:

  • applyToEither:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
  • acceptEither: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
  • runAfterEither: 不会把执行结果当做方法入参,且没有返回值。

所有都匹配

所有任务都执行完成后,才执行 allOf返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture,执行get方法,会抛出异常

任意一个匹配

任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行的任务异常,anyOf的CompletableFuture,执行get方法,会抛出异常

thenCompose

thenCompose方法会在某个任务执行完成后,将该任务的执行结果,作为方法入参,去执行指定的方法。该方法会返回一个新的CompletableFuture实例

  • 如果该CompletableFuture实例的result不为null,则返回一个基于该result新的CompletableFuture实例;
  • 如果该CompletableFuture实例为null,然后就执行这个新任务

结尾

CompletableFuture 优秀的地方之一就是它的任务的编排。比如:先完成a,再完成b;a和b同时完成再做c,满足各种各样的业务组合方式。那么,好好利用编排功能完成工作中的难点吧。

标签:异步,Java,System,任务,CompletableFuture,println,执行,out
From: https://blog.51cto.com/u_15424046/6174281

相关文章

  • 如何在Java中做基准测试?JMH使用初体验
    大家好,我是王有志,欢迎和我聊技术,聊漂泊在外的生活。快来加入我们的Java提桶跑路群:共同富裕的Java人。最近公司在搞新项目,由于是实验性质,且不会直接面对客户的项目,这次的技术选型非常激进,如,直接使用了Java17。作为公司里练习两年半的个人练习生,我自然也是深度的参与到了技术选型的......
  • 实战-JAVA应用程序CPU占用率飙升,定位线程的堆栈信息
    分以下几个步奏:(1)使用命令top-p<pid>,显示你的java进程的cpu情况,pid是你的java进程号,比如14203。(使用jps可以获取到java的进程id或者top直接查看)(2)按H,获取每个线程的CPU情况。(shirt+H)(3)找到内存和cpu占用最高的线程tid,比如14204。(4)转为十六进制得到377C,此为线程id的十六进......
  • 什么是 Java 字节码?采用字节码的好处是什么?
    在Java中,JVM可以理解的代码就叫做字节码(即扩展名为.class的文件),它不面向任何特定的处理器,只面向虚拟机。Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以,Java程序运行时相对来说还是高效的(不过,和C++......
  • Java 判断是否是数字 正则表达式
    privatestaticbooleanisInteger(Stringstr){//可以包含小数Patternpattern=Pattern.compile("^[0-9]+(.[0-9]+)?$");//只包含两位小数Patternpattern=Pattern.compile("^(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?$");......
  • javascript把本地sql数据库表转换为对象
    在做项目的时候,需要读取本地数据库,并且在页面上显示出来,原始数据读取出来的原始数据如下:varr=sqliteDB.exec(document.getElementById('txtSQL').value); console.info(r);  使用系统的转换方式console.log('Hereisarow:'+JSON.stringify(r));  可以看到其......
  • 【Java 并发】【九】【AQS】【七】Semaphore信号量底层机制原理
    1 前言接下来我们来看看Semaphore,也是基于之前讲解的AQS来实现的,建立在AQS体系之上的一个并发工具类。2  Semaphore是什么Semaphore,它是一个信号量,主要作用是用来控制并发中同一个时刻执行的线程数量,可以用来做限流器,或者流程控制器。在创建的时候会指定好它有多少个信号量......
  • xhEditor粘贴图片自动上传到服务器(Java版)
    ​ 当前功能基于PHP,其它语言流程大致相同 1.新增上传wordjson配置在ueditor\php\config.json中新增如下配置:     /* 上传word配置 */    "wordActionName":"wordupload",/* 执行上传视频的action名称 */    "wordFieldName":"upfile",/* 提交的......
  • 你不会还不知道JavaScript常用的8大设计模式?
    JavaScript常用的8大设计模式有工厂模式:工厂模式是一种创建对象的模式,可以通过一个共同的接口创建不同类型的对象,隐藏了对象的创建过程。单例模式:单例模式是一种只允许实例化一次的对象模式,可以通过一个全局访问点来访问它。建造者模式:建造者模式是一种创建复杂对象的模式,通......
  • dedecms粘贴图片自动上传到服务器(Java版)
    ​图片的复制无非有两种方法,一种是图片直接上传到服务器,另外一种转换成二进制流的base64码目前限chrome浏览器使用首先以um-editor的二进制流保存为例:打开umeditor.js,找到UM.plugins['autoupload'],然后找到autoUploadHandler方法,注释掉其中的代码。加入下面的代码://判断剪贴......
  • java基础——静态代理和动态代理
    java代理模式有静态代理和动态代理两种实现方式一、静态代理代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。优点:可以在不修改目标对象的前提下扩展目标对象的功能......