Java实现异步的几种方式

2023-10-29

Java实现异步的几种方式

异步编程在对响应时间近乎严苛的今天,受到了越来越多的关注,尤其是在IO密集型业务中。对比传统的同步模式,异步编程可以提高服务器的响应时间和处理业务的能力,从而达到快速给用户响应的效果。

代码前置:方法中会直接使用到线程池和print函数

public class TestAsync {
    // 创建一个线程池,大小为10
    ExecutorService executorService = Executors.newFixedThreadPool(10);

    private String printThread(String message) {
        return Thread.currentThread().getName() + " " + message;
    }
    //下面的示例代码...
}

1 Future

①介绍

  • java.util.concurrent.Future是JDK5引入的,用来获取一个异步计算的结果。可以使用isDone方法检查计算是否完成,也可以使用get阻塞住调用线程,直到计算完成返回结果,使用cancel方法停止任务的执行。
  • FutureTask.java是对Futre和Runnable最简单的实现,实现了run函数,所以可以直接执行,任务执行结束通过set()保存结果,setException()保存异常信息。通常配合executorService.submit()一起使用,ExecutorService中将任务包装成FutureTask执行execute();

样例如下图:

在这里插入图片描述

②代码实现

public class TestAsync {
    // 创建一个线程池,大小为10
    ExecutorService executorService = Executors.newFixedThreadPool(10);

    private String printThread(String message) {
        return Thread.currentThread().getName() + " " + message;
    }

    /**
     * 小明需要等待厨师炒好菜
     * @throws InterruptedException
     * @throws ExecutionException
     */
    @Test
    public void futureCallBackTest() throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        System.out.println(printThread("小明点餐"));
        Future<String> future = executorService.submit(() -> {
            System.out.println(printThread("厨师开始炒菜"));
            Thread.sleep(2000);
            System.out.println(printThread( "厨师炒好菜"));
            return "饭菜好了";
        });
        //休眠5s,模拟其他业务代码执行【可以执行其他操作,而不受厨师炒菜的影响】
        Thread.sleep(5000);
        String result = future.get();
        executorService.shutdown();
        System.out.println(printThread(result + ",小明开始吃饭"));
        //long end = System.currentTimeMillis();
        //long res = end - start;
        //System.out.println("耗费时间:" + res);
    }
}

③结果和分析

  • 运行结果:
    在这里插入图片描述
  • 优缺点:
  1. 能获取异步线程执行结果
  2. 无法方便得知任务何时完成
  3. 在主线程获取任务的过程中会导致主线程阻塞
  4. 复杂一点的情况下,比如多个异步任务的场景,一个异步任务依赖上一个异步任务的执行结果,异步任务合并等,Future无法满足需求

在这里插入图片描述

2 ListenableFuture(Guava)

①介绍

  • Google框架中的Guava 提供了 ListenableFuture 类来执行异步操作
  • ListenableFuture对Java原生的Future做了扩展,顾名思义就是使用了监听器模式实现了回调
  • 通过addListener(Runnable listener, Executor executor)方法添加回调任务。
  • 要使用listenableFuture还要结合MoreExecutor线程池,MoreExecutor是对Java原生线程池的封装,比如常用的MoreExecutors.listeningDecorator(threadPool); 修改Java原生线程池的submit方法,封装了future返回listenableFuture。

添加maven依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.2-jre</version>
</dependency>

样例:
在这里插入图片描述

②代码

/**
 * Google的guava
 * @throws InterruptedException
 * @throws ExecutionException
 */
@Test
public void listenableFutureTest() throws InterruptedException, ExecutionException {
    System.out.println(printThread("小明点餐"));
    ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
    ListenableFuture<String> listenableFuture = listeningExecutorService.submit(() -> {
        System.out.println(printThread("厨师开始炒菜"));
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(printThread( "厨师炒好菜"));
        return "饭菜好了";
    });

    Futures.addCallback(listenableFuture, new FutureCallback<String>() {
        @Override
        public void onSuccess(@Nullable String s) {
            System.out.println(printThread(s + ",小明开始吃饭"));
        }

        @Override
        public void onFailure(Throwable throwable) {
            System.out.println(printThread( throwable.getMessage()));
        }
    }, executorService);

    System.out.println(printThread( "小明开始玩游戏"));
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(printThread("小明结束玩游戏"));


    listenableFuture.get();
    listeningExecutorService.shutdown();
    executorService.shutdown();
}

③结果和分析

  • 运行结果
    在这里插入图片描述

这里除main线程外,重新创建了两个线程,没有阻塞等待

  • 优缺点:
  • 充分利用了时间片,只要任务结束就马上响应
  • 但是可能会导致Callback Hell(回调地狱)
    在这里插入图片描述
    例如,我们可以通过回调实现如下逻辑:
    在这里插入图片描述
    但是该方法会导致代码难以理解同时不易于维护,由于代码过多,此处以图展现

在这里插入图片描述

3 CompleteableFuture

①介绍

  • Java8的新类,借鉴了Google Guava的ListenableFuture
  • CompleteableFuture可以用声明式语义来创建多种异步任务:
    1. 创建一个异步任务
    2. 创建一个异步任务,并且该任务必须在一个前驱异步任务之后执行,其以前驱任务的输出作为自身的输入
    3. 创建一个异步任务,且该任务必须在若干个前驱异步任务中的(任意或全部)完成之后执行,其以全部(或任一)前驱任务的输出作为自身的输入

样例:
在这里插入图片描述

②代码

@Test
    public void completeableFutureTest()  {
        System.out.println(printThread("小明点餐"));

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(printThread("厨师开始做菜"));
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(printThread("厨师菜做好了"));
            return "菜已装盘";
        });

        CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(() -> {
            System.out.println(printThread( "小明开始玩游戏"));
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(printThread("小明结束玩游戏"));
        });

        CompletableFuture<Void> completableFuture2 = completableFuture
                .thenAcceptBoth(completableFuture1,(a, b) -> System.out.println(printThread( a + ", 小明开始吃饭,并点了饮料")))
                .thenApplyAsync((b) -> {
                    System.out.println(printThread("服务员拿饮料"));
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "饮料好了";
                },executorService)
                .thenAcceptAsync((s) -> System.out.println(printThread(s + ",小明开始喝饮料")));

        completableFuture2.join();
    }

③结果和分析

  • 运行结果
    在这里插入图片描述
  • 优缺点:

可以看到我们可以让通过监听的方式让线程实现某种业务顺序

4 Reactor

①介绍

  • Reactor 框架是 Pivotal 公司( Spring 家族公司)开发的,实现了 Reactive Programming 思想,符合 Reactive Streams 规范的一项技术;
  • Reactor 是一个高度可伸缩、内存利用率高、灵活性强的响应式编程框架,有助于提高应用程序的性能和稳定性。
  • Reactor 是基于流式编程的一种响应式编程框架。它实现了响应式流 API,可以处理并发数据的异步流,并支持通过多种操作符进行流式转换和操作。与传统的命令式编程范式不同,响应式编程可以更容易地实现异步、响应式、非阻塞和可响应式等特性,并且在处理IO密集型应用程序时具有明显的优势。
  • 主要接口:
    1. Publisher
    2. Subscriber
    3. Subcription
      其中,Subcriber 中便包含了非常重要的 onNext、onError、onCompleted 这三个方法。
      在这里插入图片描述
      导入maven依赖:
<!--reactor-->
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <version>3.2.3.RELEASE</version>
    <scope>test</scope>
</dependency>

②代码

public class TestReactor {

    public static void main(String[] args) throws InterruptedException {
        Flux.just(1, 2, 3, 4, 5)
                .subscribeOn(Schedulers.parallel())
                .subscribe(new CoreSubscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        System.out.println(printThread("onSubscribe, " + s.getClass().toString()));
                        s.request(5);
                    }

                    @Override
                    public void onNext(Integer integer) {
                        System.out.println(printThread("next: " + integer));
                    }

                    @Override
                    public void onError(Throwable t) {

                    }

                    @Override
                    public void onComplete() {
                        System.out.println(printThread("complete"));
                    }
                });
        Thread.sleep(1000);
    }

    private static String printThread(String note) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("hh:mm:ss");
        long time = System.currentTimeMillis();
        Date date = new Date(time);
        return Thread.currentThread().getName() + " " + simpleDateFormat.format(date) + " " + time + " " + note;
    }
}

③结果和分析

  • 运行结果:
    在这里插入图片描述
  • 分析

可以看到输出结果是按照一定顺序的

  • 我们可以根据流式编程来处理很多复杂的业务逻辑

5 Java回调方式

①介绍

回调函数机制:回调函数是一种面向事件或消息的程序设计方法,它通过将函数对象作为参数传递给其他函数或对象来实现异步操作。在 Java 中,可以使用接口或匿名类来实现回调函数机制。

②代码

public class AsyncTask {
  
  // 定义回调接口
  public interface Callback {
    void onCompleted(String result);
  }
  
  // 模拟一个耗时操作
  public void doTask(final Callback callback) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        // 在新的线程中执行异步任务,模拟一个耗时操作,即等待 3 秒
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        // 返回一个结果给回调函数
        callback.onCompleted("AsyncTask completed!");
      }
    }).start();
  }
  
  // 测试代码
  public static void main(String[] args) {
    // 创建一个 AsyncTask 实例
    AsyncTask asyncTask = new AsyncTask();
    
    // 调用 doTask 方法并传入回调函数
    asyncTask.doTask(new Callback() {
      @Override
      public void onCompleted(String result) {
        // 在回调函数中打印异步任务的结果
        System.out.println(result);
      }
    });
    
    // 再做一些其他操作
    // ...
  } 
}

③结果和分析

  • AsyncTask 类中的 doTask 方法模拟了一个耗时操作,并通过回调函数的方式实现了异步。在主程序中,我们创建一个 AsyncTask 实例并调用它的 doTask 方法来执行异步任务,同时传入一个回调函数,在回调函数中打印异步任务的结果。在执行异步任务的同时,我们还可以继续进行其他操作。这样可以避免阻塞主线程,提高程序的并发性能。
  • 总之,回调函数是 Java 中一种实现异步操作的简单、方便的方式,可以帮助我们在异步执行的任务完成后及时处理结果,提高程序的响应性和并发性能。

6 Java实现异步方式

  1. 回调函数机制:回调函数是一种面向事件或消息的程序设计方法,它通过将函数对象作为参数传递给其他函数或对象来实现异步操作。在 Java 中,可以使用接口或匿名类来实现回调函数机制。

  2. Future 和 CompletableFuture:Future 是 Java 中的一种异步编程模型,它允许调用者异步地等待另一个线程的执行结果。CompletableFuture 是 Java 8 引入的一种强化版 Future,可以更方便地实现异步事件的组合、异常处理等操作。

  3. 线程池和多线程:在 Java 中,可以使用线程池和多线程来异步执行任务。通过将任务提交给线程池,可以让执行过程异步地执行,并且可以利用多个线程来处理任务,提高程序的并发性能。

  4. RxJava:RxJava 是一个基于 ReactiveX 编程模型的响应式编程库,在处理异步事件流时具有很高的效率和灵活性。RxJava 提供了多种操作符用于处理数据流,包括过滤、转换、组合等。

参考文章:
https://blog.csdn.net/mowushenght/article/details/122211330

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java实现异步的几种方式 的相关文章

  • Java 中的 XPath 节点集

    我在 eclipse 中有这段代码 NodeSet nodes NodeSet xPath evaluate expression inputSource XPathConstants NODESET 它给我 NodeSet 上的编译时错误
  • 如何使用 FileChannel 将一个文件的内容附加到另一个文件的末尾?

    File a txt好像 ABC File d txt好像 DEF 我正在尝试将 DEF 附加到 ABC 所以a txt好像 ABC DEF 我尝试过的方法总是完全覆盖第一个条目 所以我总是最终得到 DEF 这是我尝试过的两种方法 File
  • AES 加密 Java/plsql

    我需要在Java和plsql DBMS CRYPTO for Oracle 10g 上实现相同的加密 解密应用程序 两种实现都工作正常 但这里的问题是我对相同纯文本的加密得到了不同的输出 下面是用于加密 解密过程的代码 Java 和 PLS
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • manifest.mf 文件的附加内容的约定?

    Java JAR 中的 MANIFEST MF 文件是否有任何超出 MANIFEST MF 约定的约定 JAR规范 http download oracle com javase 1 4 2 docs guide jar jar html
  • 如何查找 Android 设备中的所有文件并将它们放入列表中?

    我正在寻求帮助来列出 Android 外部存储设备中的所有文件 我想查找所有文件夹 包括主文件夹的子文件夹 有办法吗 我已经做了一个基本的工作 但我仍然没有得到想要的结果 这不起作用 这是我的代码 File files array file
  • IntelliJ IDEA 创建的 JAR 文件无法运行

    我在 IntelliJ 中编写了一个跨越几个类的程序 当我在 IDE 中测试它时它运行良好 但是 每当我按照教程将项目制作成 jar 可执行文件时 它就不会运行 双击 out 文件夹中的文件时 该文件不会运行 并显示 无法启动 Java J
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li
  • 尝试使用 Ruby Java Bridge (RJB) gem 时出现错误“无法创建 Java VM”

    我正在尝试实现 Ruby Java Bridge RJB gem 来与 JVM 通信 以便我可以运行 Open NLP gem 我在 Windows 8 上安装并运行了 Java 所有迹象 至少我所知道的 都表明 Java 已安装并可运行
  • 使用 Flyway 和 Hibernate 的 hbm2ddl 在应用程序的生命周期中管理数据库模式

    我正在开发 Spring Hibernate MySql 应用程序 该应用程序尚未投入生产 我目前使用 Hibernatehbm2ddl该功能对于管理域上的更改非常方便 我也打算用Flyway用于数据库迁移 在未来的某个时候 该应用程序将首
  • 应用程序关闭时的倒计时问题

    我制作了一个 CountDownTimer 代码 我希望 CountDownTimer 在完成时重新启动 即使应用程序已关闭 但它仅在应用程序正在运行或重新启动应用程序时重新启动 因此 如果我在倒计时为 00 10 分钟 秒 时关闭应用程序
  • 使用 SAX 进行 XML 解析 |如何处理特殊字符?

    我们有一个 JAVA 应用程序 可以从 SAP 系统中提取数据 解析数据并呈现给用户 使用 SAP JCo 连接器提取数据 最近我们抛出了一个异常 org xml sax SAXParseException 字符引用 是无效的 XML 字符
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • Windows 上的 Nifi 命令

    在我当前的项目中 我一直在Windows操作系统上使用apache nifi 我已经提取了nifi 0 7 0 bin zip文件输入C 现在 当我跑步时 bin run nifi bat as 管理员我在命令行上看到以下消息 但无法运行
  • 如何配置eclipse以保持这种代码格式?

    以下代码来自 playframework 2 0 的示例 Display the dashboard public static Result index return ok dashboard render Project findInv
  • com.jcraft.jsch.JSchException:身份验证失败

    当我从本地磁盘上传文件到远程服务器时 出现这样的异常 com jcraft jsch JSchException Auth fail at org apache tools ant taskdefs optional ssh Scp exe
  • KeyPressed 和 KeyTyped 混淆[重复]

    这个问题在这里已经有答案了 我搜索过之间的区别KeyPressedand KeyTyped事件 但我仍然不清楚 我发现的一件事是 Keypressed 比 KeyTyped 首先被触发 请澄清一下这些事件何时被准确触发 哪个适合用于哪个目的
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个

随机推荐

  • MySQL实操(四)——使用Haproxy+keeplived实现Mycat高可用

    MySQL实操系列 MySQL实操 一 CentOS7安装MySQL5 7及基础配置 DreamEhome的博客 CSDN博客 centos7 中安装mysql5 7配置表名忽略大小写 MySQL实操 二 MySQL主从同步实战 Dream
  • Labview操作串口-----------通过VISA驱动

    本篇文章将会详细介绍如何通过labview的VISA驱动模块来操作PC的串口 基于LABVIEW2012 VISA530full exe 1 首先需要安装LABVIEW 接下里安装VISA530驱动模块 否则编译本代码时会出现缺少VISA驱
  • STM32 四轴无人机设计——PPM控制无人机动作

    1 前言 前面两次我们成功的将PPM信号的读取 储存和油门处理完成了 这次我们要开始真正的控制无人机 首先我们要了解无人机前后 左右 旋转 升降的原理 2 动作原理 引用 https blog csdn net weixin 3570379
  • flutter get 命令行工具

    mac终端执行 flutter pub global activate get cli 这个安装完以后会提示英文提示你要把一个 目录添加到mac环境变量中 下面的目录是你安装完以后提示你放到环境变量里的目录 export PATH PATH
  • [MacOs]用外置硬盘制作MacOs系统盘

    文章目录 背景 设备 步骤 下载指定系统 制作启动盘 安装系统 背景 最近我的mac air出了点问题 经检查应该是硬盘问题 由于最近得待在老家没办法维修又需要用 只能用外置的硬盘做个系统盘来将就一下使用 设备 需要两个存储设备 不小于32
  • 马上:头疼:安卓黑屏,白屏,网卡,sdcard挂载等问题深入分析解决

    白屏 设备一段时间使用后 白屏 需要重启 经线上日志和以下相关代码初步分析是AMS 窗口显示问题 检查相关业务代码 发现Activity有可能被finish多次 导致失败 还有多次startActivity if isFinishing r
  • MySQL数据库误删回滚

    某次一不小心 用了delete from xxx 删除了几条重要数据 在网上找了很多方法 但都比较零散 打算记录本次数据找回的过程 大致分为以下几步 1 查看binlog是否开启 log bin是ON 就说明打开了 OFF就是关闭状态 以下
  • linux查看nginx安装路径

    linux查看nginx安装路径 有几种方法可以查看nginx的安装路径 使用which命令 which nginx 这个命令会返回nginx的二进制文件路径 一般也是安装路径 查看nginx的进程 得到安装路径 ps aux grep n
  • python爬虫高级教程,JS逆向之百度翻译

    环境 python版本号 系统 游览器 python 3 7 2 win7 google chrome 关于本文 本文将会通过爬虫的方式实现简单的百度翻译 本文中的代码只供学习 不允许作为于商务作用 商务作用请前往api fanyi bai
  • 浅谈控制反转(IoC)

    Inversion of Control 什么是控制反转 程序的流程控制权相对于传统的面向过程编程而言发生了反转 下面是维基百科的描述 In software engineering inversion of control IoC is
  • jest测试ajax,搭建Jest前端测试框架总结

    先说一下我要使用Jest的原因 由于开发提测了一个js的公共组件 需要测试人员对此组件的功能进行测试 因为提测的直接就是js文件 所以我们也就只能对其中的方法进行类白盒测试 知道了为什么测 那接下来就是怎么测 很容易的想到了需要一个测试框架
  • 60-200-040-使用-命令-MySQL查看引擎的命令

    文章目录 1 查看存储引擎 2 MySAM 和 InnoDB对比 1 查看存储引擎 mysql gt show ENGINES Engine Support
  • 调制与解调(1)——初认识

    在深入项目前 还需要对调制解调做深入学习 1 基本概念 调制 调制就是使一个信号 如光 高频电磁振荡等 的某些参数 如振幅 频率等 按照另一个欲传输的信号 如声音 图像等 的特点变化的过程 调制是通过改变高频载波的幅度 相位或者频率 使其随
  • 基于VS2019配置opencv4.0

    文章目录 1 前言 2 不说废话 直接上图干净利落 2 1 创建新空白项目 2 2 添加一个主文件 2 3 配置opencv环境 2 4 链接器配置 2 5 将opencv添加到计算机环境中 2 6 文件复制 3 运行测试环境 1 前言 不
  • 安信可SX1278LORA通讯试验

    LoRa 的名字是远距离无线电 Long Range Radio 作为一种线性调频扩频的调制技术 最早由法 国几位年轻人创立的一家创业公司 Cycleo 推出 2012 年 Semtech 收购了这家公司 并将这一调制技术 封装到芯片中 基
  • 3D游戏第八次作业

    3D游戏第八次作业 一 简单粒子制作 按参考资源要求 制作一个粒子系统 参考资源 使用 3 3 节介绍 用代码控制使之在不同场景下效果不一样 1 模拟烟花发射 效果展示 实现 给空对象挂载一个名为moveup的粒子系统模拟烟花发射 Emis
  • java中对象属性可以是另外一个对象或对象的参考

    7 对象的属性可以是另外一个对象或对象的参考 通过这种方法可以迅速构建一个比较大的系统 class Motor Light lights Handle left right KickStart ks Motor lights new Lig
  • 改变MySQL的默认编码

    etc mysql my cnf mysqld character set server utf8 collation server utf8 unicode ci init connect SET collation connection
  • 论文阅读-Exploring Frequency Adversarial Attacks for Face Forgery Detection(探索用于人脸伪造检测的频率对抗性攻击)

    一 论文信息 论文名称 Exploring Frequency Adversarial Attacks for Face Forgery Detection 会议 CVPR 2022 作者团队 二 动机 虽然现有的人脸伪造分类器在检测伪造图
  • Java实现异步的几种方式

    Java实现异步的几种方式 异步编程在对响应时间近乎严苛的今天 受到了越来越多的关注 尤其是在IO密集型业务中 对比传统的同步模式 异步编程可以提高服务器的响应时间和处理业务的能力 从而达到快速给用户响应的效果 代码前置 方法中会直接使用到