Spring Boot中使用@Async实现异步调用

什么是“异步调用”?

“异步调用”对应的是“同步调用”,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。

同步调用

下面通过一个简单示例来直观的理解什么是同步调用:

  • 定义Task类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)
@Component
public class Task {

public static Random random =new Random();

public void doTaskOne() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}

public void doTaskTwo() throws Exception {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}

public void doTaskThree() throws Exception {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}

}
  • 在单元测试用例中,注入Task对象,并在测试用例中执行doTaskOnedoTaskTwodoTaskThree三个函数。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ApplicationTests {

@Autowired
private Task task;

@Test
public void test() throws Exception {
task.doTaskOne();
task.doTaskTwo();
task.doTaskThree();
}

}
  • 执行单元测试,可以看到类似如下输出:
开始做任务一
完成任务一,耗时:4256毫秒
开始做任务二
完成任务二,耗时:4957毫秒
开始做任务三
完成任务三,耗时:7173毫秒

任务一、任务二、任务三顺序的执行完了,换言之doTaskOnedoTaskTwodoTaskThree三个函数顺序的执行完成。

异步调用

上述的同步调用虽然顺利的执行完了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。

在Spring Boot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数,Task类改在为如下模式:

@Component
public class Task {

@Async
public void doTaskOne() throws Exception {
// 同上内容,省略
}

@Async
public void doTaskTwo() throws Exception {
// 同上内容,省略
}

@Async
public void doTaskThree() throws Exception {
// 同上内容,省略
}

}

为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync,如下所示:

@SpringBootApplication
@EnableAsync
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

此时可以反复执行单元测试,您可能会遇到各种不同的结果,比如:

  • 没有任何任务相关的输出
  • 有部分任务相关的输出
  • 乱序的任务相关的输出

原因是目前doTaskOnedoTaskTwodoTaskThree三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况。

注: @Async所修饰的函数不要定义为static类型,这样异步调用不会生效

异步回调

为了让doTaskOnedoTaskTwodoTaskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。

那么我们如何判断上述三个异步调用是否已经执行完成呢?我们需要使用Future<T>来返回异步调用的结果,就像如下方式改造doTaskOne函数:

@Async
public Future<String> doTaskOne() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务一完成");
}

按照如上方式改造一下其他两个异步函数之后,下面我们改造一下测试用例,让测试在等待完成三个异步调用之后来做一些其他事情。

@Test
public void test() throws Exception {

long start = System.currentTimeMillis();

Future<String> task1 = task.doTaskOne();
Future<String> task2 = task.doTaskTwo();
Future<String> task3 = task.doTaskThree();

while(true) {
if(task1.isDone() && task2.isDone() && task3.isDone()) {
// 三个任务都调用完成,退出循环等待
break;
}
Thread.sleep(1000);
}

long end = System.currentTimeMillis();

System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");

}

看看我们做了哪些改变:

  • 在测试用例一开始记录开始时间
  • 在调用三个异步函数的时候,返回Future<String>类型的结果对象
  • 在调用完三个异步函数之后,开启一个循环,根据返回的Future<String>对象来判断三个异步函数是否都结束了。若都结束,就结束循环;若没有都结束,就等1秒后再判断。
  • 跳出循环之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。

执行一下上述的单元测试,可以看到如下结果:

开始做任务一
开始做任务二
开始做任务三
完成任务三,耗时:37毫秒
完成任务二,耗时:3661毫秒
完成任务一,耗时:7149毫秒
任务全部完成,总耗时:8025毫秒

可以看到,通过异步调用,让任务一、二、三并发执行,有效的减少了程序的总运行时间。

代码示例

本文的相关例子可以查看下面仓库中的chapter4-1-2目录:

  • Github:https://github.com/dyc87112/SpringBoot-Learning
  • Gitee:https://gitee.com/didispace/SpringBoot-Learning

如果您觉得本文不错,欢迎Star支持,您的关注是我坚持的动力!


money.jpg

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

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

相关文章

谷歌HuggingFace| 零样本能力最强的语言模型结构

文 | iven从 GPT3 到 Prompt&#xff0c;越来越多人发现大模型在零样本学习&#xff08;zero-shot&#xff09;的设定下有非常好的表现。这都让大家对 AGI 的到来越来越期待。但有一件事让人非常疑惑&#xff1a;19 年 T5 通过“调参”发现&#xff0c;设计预训练模型时&#x…

程序员面试金典 - 面试题 01.07. 旋转矩阵(一次遍历+位运算)

1. 题目 给定一幅由N N矩阵表示的图像&#xff0c;其中每个像素的大小为4字节&#xff0c;编写一种方法&#xff0c;将图像旋转90度。 不占用额外内存空间能否做到&#xff1f; 示例 1: 给定 matrix [[1,2,3],[4,5,6],[7,8,9] ],原地旋转输入矩阵&#xff0c;使其变为: […

Spring Boot中使用@Scheduled创建定时任务

我们在编写Spring Boot应用中经常会遇到这样的场景&#xff0c;比如&#xff1a;我需要定时地发送一些短信、邮件之类的操作&#xff0c;也可能会定时地检查和监控一些标志、参数等。 创建定时任务 在Spring Boot中编写定时任务是非常简单的事&#xff0c;下面通过实例介绍如…

从二本到ICLR杰出论文奖,我用了20年

文 | 李梅编 | 陈彩娴源 | AI科技评论二本出身&#xff0c;读了两个硕士才在29岁开始读博&#xff0c;39岁才结束博士后研究的付杰形容&#xff0c;他的20年就像个体与系统的博弈&#xff1a;一些机器学习的文章中&#xff0c;研究者会根据训练初始阶段 Training Curve&#xf…

Spring Boot属性配置文件详解

相信很多人选择Spring Boot主要是考虑到它既能兼顾Spring的强大功能&#xff0c;还能实现快速开发的便捷。我们在Spring Boot使用过程中&#xff0c;最直观的感受就是没有了原来自己整合Spring应用时繁多的XML配置内容&#xff0c;替代它的是在pom.xml中引入模块化的Starter PO…

程序员面试金典 - 面试题 01.08. 零矩阵

1. 题目 编写一种算法&#xff0c;若M N矩阵中某个元素为0&#xff0c;则将其所在的行与列清零。 示例 1&#xff1a; 输入&#xff1a; [[1,1,1],[1,0,1],[1,1,1] ] 输出&#xff1a; [[1,0,1],[0,0,0],[1,0,1] ]示例 2&#xff1a; 输入&#xff1a; [[0,1,2,0],[3,4,5,2]…

AI帮写代码67元/月!

整理 | 彭慧中责编 | 屠敏出品 | CSDN如今&#xff0c;人工智能已经逐渐习惯充当人类生活中“副驾驶”位置上的角色。它帮助我们打扫卫生、撰写文稿、回复消息、路线导航....但在此之前&#xff0c;人工智能在改进代码方面还止步不前&#xff0c;以至于多少人还在为绞尽脑汁写代…

Spring Boot中Web应用的统一异常处理

我们在做Web应用的时候&#xff0c;请求处理过程中发生错误是非常常见的情况。Spring Boot提供了一个默认的映射&#xff1a;/error&#xff0c;当处理中抛出异常之后&#xff0c;会转到该请求中处理&#xff0c;并且该请求有一个全局的错误页面用来展示异常内容。 选择一个之…

统计学习及监督学习概论

文章目录1. 统计学习2. 统计学习分类2.1 基本分类2.1.1 监督学习 supervised learning2.1.2 无监督学习 unsupervised learning2.1.3 强化学习 reinforcement learning2.1.4 半监督学习 semi-supervised learning、主动学习 active learning2.2 按模型分类2.3 按算法分类2.4 按…

BERT为何无法彻底干掉BM25??

文 | QvQ近些年来&#xff0c;相比传统检索模型&#xff0c;大规模预训练式transformers结构的引入在各类任务上都有显著的提升。而这种提升在不同的数据集上有着特殊的模型设置&#xff0c;而当前依旧无法充分理解这些模型为什么以及如何可以更好的工作。古人云&#xff1a;知…

Spring Boot中使用MongoDB数据库

前段时间分享了关于Spring Boot中使用Redis的文章&#xff0c;除了Redis之后&#xff0c;我们在互联网产品中还经常会用到另外一款著名的NoSQL数据库MongoDB。 下面就来简单介绍一下MongoDB&#xff0c;并且通过一个例子来介绍Spring Boot中对MongoDB访问的配置和使用。 Mong…

程序员面试金典 - 面试题 01.09. 字符串轮转

1. 题目 字符串轮转。给定两个字符串s1和s2&#xff0c;请编写代码检查s2是否为s1旋转而成&#xff08;比如&#xff0c;waterbottle是erbottlewat旋转后的字符串&#xff09;。 示例1:输入&#xff1a;s1 "waterbottle", s2 "erbottlewat"输出&#x…

谷歌HuggingFace | 零样本能力最强的语言模型结构

从 GPT3 到 Prompt&#xff0c;越来越多人发现大模型在零样本学习&#xff08;zero-shot&#xff09;的设定下有非常好的表现。这都让大家对 AGI 的到来越来越期待。但有一件事让人非常疑惑&#xff1a;19 年 T5 通过“调参”发现&#xff0c;设计预训练模型时&#xff0c;Enco…

Spring Boot中使用Redis数据库

Spring Boot中除了对常用的关系型数据库提供了优秀的自动化支持之外&#xff0c;对于很多NoSQL数据库一样提供了自动化配置的支持&#xff0c;包括&#xff1a;Redis, MongoDB, Elasticsearch, Solr和Cassandra。 使用Redis Redis是一个开源的使用ANSI C语言编写、支持网络、…

程序员面试金典 - 面试题 02.01. 移除重复节点(哈希set)

1. 题目 编写代码&#xff0c;移除未排序链表中的重复节点。保留最开始出现的节点。 示例1:输入&#xff1a;[1, 2, 3, 3, 2, 1]输出&#xff1a;[1, 2, 3]示例2:输入&#xff1a;[1, 1, 1, 1, 2]输出&#xff1a;[1, 2]提示&#xff1a; 链表长度在[0, 20000]范围内。 链表元…

从 Google AI 离职了,这里让我爱不起来

文 | 天于刀刀大家都清楚&#xff0c;谷歌是一所著名的能让无数打工人「爱上工作」的良心企业。轻松自由的办公环境&#xff0c;超多在职福利和活动&#xff0c;更不必说业界第一的金字招牌带来的丰厚报酬&#xff0c;是吾辈躺平咸鱼的终极 Dream Offer 。而现在竟然有人主动提…

Spring Boot日志管理

Spring Boot在所有内部日志中使用Commons Logging&#xff0c;但是默认配置也提供了对常用日志的支持&#xff0c;如&#xff1a;Java Util Logging&#xff0c;Log4J, Log4J2和Logback。每种Logger都可以通过配置使用控制台或者文件输出日志内容。 格式化日志 默认的日志输出…

感知机(Perceptron)

文章目录1. 感知机模型2. 感知机学习策略3. 感知机学习算法3.1 原始形式3.2 算法收敛性3.3 对偶形式4. 基于感知机Perceptron的鸢尾花分类实践感知机&#xff08;perceptron&#xff09;是 二类分类的线性分类模型输入&#xff1a;实例的特征向量输出&#xff1a;实例的类别&am…

苹果5G芯片研发失败,冲上热搜

文 | 科小编源 | 科技每日推送多年来&#xff0c;iPhone的信号问题&#xff0c;一直被大家诟病。苹果也一直尝试自研5G芯片来解决&#xff0c;但最新消息显示&#xff0c;苹果再次失败了。6月28日&#xff0c;有“地表最强苹果剧透师”之称的郭明錤&#xff0c;在推特上爆料&am…

Spring Boot多数据源配置与使用

之前在介绍使用JdbcTemplate和Spring-data-jpa时&#xff0c;都使用了单数据源。在单数据源的情况下&#xff0c;Spring Boot的配置非常简单&#xff0c;只需要在application.properties文件中配置连接参数即可。但是往往随着业务量发展&#xff0c;我们通常会进行数据库拆分或…