SpringBoot - Async异步处理

AI摘要

本文介绍了同步调用和异步调用的定义,同步调用按顺序执行任务,而异步调用不阻塞调用者。还给出了Spring Boot中同步与异步调用的示例,包括执行类、测试用例和运行结果。异步调用可提高执行效率,通过异步回调能统计并发任务总耗时。

目录

一、定义

1、同步调用

2、异步调用

二、示例

1、同步调用

执行类:

测试用例: 

运行结果:

2、异步调用

(1)普通调用

执行类:

测试用例:

运行结果:

(2)异步回调

执行类:

测试用例:

执行逻辑:


一、定义

1、同步调用

  • 定义:同步调用是一种按照顺序依次执行任务的方式。在程序中,当一个函数或方法调用另一个函数或方法时,调用者会暂停自身的执行,等待被调用者执行完毕并返回结果后,调用者才会继续执行后续的操作。
  • 就好像一个人在排队等待服务,必须等到前面的人完成服务后,自己才能接受服务并继续下一步行动。

2、异步调用

  • 定义:异步调用是一种不阻塞调用者执行的调用方式。当一个函数或方法被异步调用时,调用者不会等待被调用者执行完毕,而是继续执行自己后续的操作。被调用者在后台独立运行,当它完成任务后,会通过某种方式(如回调函数、事件通知等)将结果返回给调用者。
  • 这就好比一个人在餐厅点了菜后,不需要一直坐在那里等待上菜,而是可以去做其他事情,等菜做好了,服务员会通知他。

二、示例

1、同步调用

执行类:

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

package com.example.springbootasync.demos.web;

import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @Author: 
 * @CreateTime: 2024-11-15
 * @Description: 同步任务执行类
 */
@Component
public class SynchronizationTask {

    public static Random random = new Random();

    public void taskOne() 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 taskTwo() 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 taskThree() throws Exception {
        System.out.println("任务三开始执行......");

        long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();

        System.out.println("任务三完成耗时:" + (end - start) + "毫秒");
    }

}
 测试用例: 

          在单元测试用例中,注入SynchronizationTask对象,并在测试用例中执行taskOne、taskTwo、taskThree三个函数。

package com.example.springbootasync;

import com.example.springbootasync.demos.web.SynchronizationTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBootAsyncApplicationTests {

    @Autowired
    private SynchronizationTask sTask;

    @Test
    void synchronizationTask() throws Exception {
        sTask.taskOne();
        sTask.taskTwo();
        sTask.taskThree();
    }

}

执行单元测试

运行结果:

任务一开始执行......
任务一完成耗时:2649毫秒
任务二开始执行......
任务二完成耗时:7083毫秒
任务三开始执行......
任务三完成耗时:5752毫秒

        可以看出三个任务是按照顺序执行的,这就是同步调用,一个任务执行完成才能执行下一个任务。

2、异步调用

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

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

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class SpringBootAsyncApplication {

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

}

(1)普通调用

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

执行类:
package com.example.springbootasync.demos.web;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

/**
 * @Author: wxzhanglinsen
 * @CreateTime: 2024-11-15
 * @Description: 异步任务执行类
 */
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async
    public void taskOne() throws Exception {
        //同上
    }

    @Async
    public void taskTwo() throws Exception {
        //同上
    }

    @Async
    public void taskThree() throws Exception {
        //同上
    }

}
测试用例:
package com.example.springbootasync;

import com.example.springbootasync.demos.web.AsyncTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBootAsyncApplicationTests {

    @Autowired
    private AsyncTask aTask;

    @Test
    void asyncTask() throws Exception {
        aTask.taskOne();
        aTask.taskTwo();
        aTask.taskThree();
    }

}
运行结果:

任务二开始执行......
任务三开始执行......
任务一开始执行......

任务二开始执行......
任务一开始执行......
任务三开始执行......

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

  • 没有任何任务相关的输出

  • 有部分任务相关的输出

  • 乱序的任务相关的输出

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

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

(2)异步回调

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

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

 执行类:
package com.example.springbootasync.demos.web;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

/**
 * @Author: wxzhanglinsen
 * @CreateTime: 2024-11-15
 * @Description: 异步任务执行类
 */
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async
    public Future<String> taskOne() 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<>("任务一完成");
    }

    @Async
    public Future<String> taskTwo() 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<>("任务二完成");
    }

    @Async
    public Future<String> taskThree() 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<>("任务三完成");
    }
}
测试用例:
package com.example.springbootasync;

import com.example.springbootasync.demos.web.AsyncTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.Future;

@SpringBootTest
class SpringBootAsyncApplicationTests {

    @Autowired
    private AsyncTask aTask;

    @Test
    void asyncTask() throws Exception {
        long start = System.currentTimeMillis();

        Future<String> task1 = aTask.taskOne();
        Future<String> task2 = aTask.taskTwo();
        Future<String> task3 = aTask.taskThree();

        while(true) {

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

            //停隔1s再进行扫描,扫描任务是否都已经完成
            Thread.sleep(1000);
        }

        long end = System.currentTimeMillis();

        System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
    }
}
执行逻辑:
  • 在测试用例一开始记录开始时间

  • 在调用三个异步函数的时候,返回Future类型的结果对象

  • 在调用完三个异步函数之后,开启一个循环,根据返回的Future对象来判断三个异步函数是否都结束了。若都结束,就结束循环;若没有都结束,就等1秒后再判断。

跳出循环之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。

运行结果:

任务一开始执行......
任务二开始执行......
任务三开始执行......
任务一完成耗时:188毫秒
任务二完成耗时:4828毫秒
任务三完成耗时:9208毫秒
任务全部完成,总耗时:10014毫秒

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

SpringBoot(十三)异步任务 阅读详情

相关推荐

SpringBoot 中的异步处理机制详解

默认情况下,Spring Boot 会使用一个简单的线程池来执行异步任务。但是在实际的生产环境中,我们通常需要对线程池进行更精细的配置。我们可以通过定义 Executor Bean 来自定义线程池。在这个配置中,我们创建了一个线程池 Executor,指定了核心线程数、最大线程数、队列容量等参数。线程池可以帮助我们更好地管理资源,避免大量并发任务导致系统资源耗尽。// 任务逻辑。

tangjieqing的博客 1108

Springboot集成Async异步调用

前言 在业务开发过程中,有时会遇到非核心的业务,如记录日志或发送短信等。有时也会遇到一次性要处理多个任务,再将多个任务处理结果进行返回。如果做同步处理时,会比较耗时,对于接口而言,响应速度慢一方面会影响用户体验,另一方面如果是服务间调用,可能会出现超时的情况。这种情况我们可以采用异步处理的方式,提高主流程的性能。 集成方法 1、springboot启动类添加@EnableAsync注解 @SpringBootApplication @EnableAsync public class Application

zhutian0991的博客 481

三相桥式整流电路有源逆变原理与应用详解

有源逆变是电力电子技术中的核心概念,实现直流电能向交流电网的回馈。其工作原理基于触发角控制,当触发角大于90度时,电路输出电压极性反转,实现能量反向流动。这项技术在节能和能量回收方面具有重要价值,广泛应用于直流电机调速系统、电梯能量回馈和新能源发电并网等场景。三相桥式整流电路通过六个晶闸管的协调控制,可以稳定工作在整流和逆变两种状态,其中逆变状态包含6种不同的工作模式,每种模式持续60度电角度。理解有源逆变与整流的区别,掌握触发角调节和波形分析,对电力电子工程师处理如起重机节能回馈、轨道交通制动能量回收等实

weixin_33801856的博客 435

SMETANA 安装使用教程

关于SMETANA的安装和简单使用信息

qq_44940977的博客 1161

【校园网代理连接问题】已解决,“该设备或资源(Web 代理)未设置为接受端口“7890”上的连接“

我遇到这个问题很多次。每次都是vpn的问题,不止通俗意义上的梯子(clash之类的),还包括校园网vpn。点击“ 拨号和虚拟专用网络设置”中的“设置”, 关闭代理服务器。在打开vpn后或者关闭vpn后无法浏览网页,网页诊断显示“关闭“手动设置代理”中的“使用代理服务器”,关闭对应的本地代理即可。

聆雨。 1万+

【后端】主流后端语言:JAVA、C、C++、GO、PYTHON

软件开发领域,语言本身在各自领域都有适用场景,有许多流行的编程语言可供选择,每种语言都有其独特的特点和适用场景。

wnm23的专栏 1万+

【计算机视觉】基于复杂环境下的车牌识别

本文针对复杂环境下(光照变化、车牌变形、噪声干扰)的车牌识别问题,提出了一套完整的优化方案。通过四个核心环节进行改进:1)采用CLAHE、同态滤波等技术优化光照处理;2)使用边缘检测和透视变换应对车牌变形;3)结合中值滤波和暗通道去噪算法消除干扰;4)改进字符分割与识别方法。文章提供了详细的Python实现代码,展示了从预处理到字符识别的完整流程,并通过多策略融合显著提升了复杂场景下的识别准确率。实验结果表明,该方法能有效应对各类复杂环境挑战,为实际应用提供了可靠解决方案。

2401_84149564的博客 1624

YOLOv11涨点改进 | Conv篇/小目标改进 | YOLOv11引入SPDConv卷积特征无损下采样模块,增加YOLOv11小目标P2检测层(助力小目标任务高效涨点)

本文提出了一种创新的SPDConv模块来优化YOLOv11模型,通过空间到深度(SPD)层和非步幅卷积层实现特征无损下采样。该方法有效解决了传统CNN中因跨步卷积和池化层导致的细粒度信息丢失问题,特别提升了小目标检测性能。实验表明,SPDConv在目标检测和图像分类任务中显著优于现有SOTA模型。文章详细介绍了模块原理、核心代码实现,并提供了YOLOv11集成SPDConv的具体方法,包括配置文件修改和P2层增强等优化技巧。该技术为计算机视觉任务提供了一种高效的特征保留方案。

Ai缝合怪、助力小伙伴高效发论文 586

AutosarMCAL开发——基于EB MCU驱动

本文为博主个人学习总结记录,如有不正,欢迎指正。

qq_45583706的博客 918

电商数据采集实战——多平台价格监控、比价分析与智能告警系统

在电商竞争日益激烈的今天,价格监控与比价分析已成为品牌商、渠道商和消费者的核心需求。对于品牌商而言,需要监控各渠道售价是否低于指导价(破价监控);对于消费者而言,希望在最佳时机以最低价购入心仪商品;对于数据分析师而言,价格波动数据是洞察市场趋势的金矿。然而,电商数据采集面临三大核心挑战:本文将基于 Python + Scrapy + Playwright + Redis + ClickHouse 技术栈,构建一套完整的电商价格监控与比价系统。架构分层说明:电商平台的反爬手段层出不穷,采集系统必须具备多层次的

从事移动开发八年,csdn博客专家,阿里云特邀专家,华为云云享专家,高级网络信息安全工程师,大数据工程师(高级),python技术开发(高级),HarmonyOS 应用开发者高级认证,现在某学校任教。 133

倍福--手动加入第三方伺服的配置

客户在没有硬件情况下,希望手动配置硬件,添加第三方驱动器,本文介绍其操作流程 目 录 软硬件版本 3 1.1. 倍福Beckhoff 3 1.1.1. 控制器硬件 3 1.1.2. 控制软件 3 操作步骤 3 2.1. 添加电机XML文件 3 2.2. 创建EtherCAT 3 2.3. 添加新建项 3 2.4. 选择型号 4 2.5. 关联NC轴 5 软硬件版本 1.1. 倍福Beckhoff 1.1.1. 控制器硬件 TwinCAT控制制器,PC或者EPC,包括: 嵌入式控制器:CX5020-

weixin_41883890的博客 5321

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

上一篇: 计算工具类
下一篇: 优雅使用 Enum 提升 SpringBoot 配置管理效率
子笙—望舒
博客等级 码龄7年 171粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子笙—望舒

整理编写不易,希望能支持支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值