【SpringBoot】20 同步调用、异步调用、异步回调

Git仓库

https://gitee.com/Lin_DH/system

介绍

同步调用:指程序在执行时,调用方需要等待函数调用返回结果后,才能继续执行下一步操作,是一种阻塞式调用。

异步调用:指程序在执行时,调用方在调用函数后立即返回,不需要等待函数调用返回结果,可以直接执行下一步操作,当函数执行完成后,会通过回调或其他方式通知调用方,得到返回结果。

回调:在调用一个函数后,需要在函数执行中或执行后,将执行结果或状态返回给调用者。

代码实现

第一步:启动类上添加 @EnableAsync 注解,开启异步功能。

java 复制代码
@EnableAsync
@SpringBootApplication
public class SystemApplication extends SpringBootServletInitializer {}

同步调用

第二步:添加同步调用业务逻辑

注:@Async 注解不能修饰的 static 修饰的函数,该类型的函数异步调用不会生效。

java 复制代码
package com.lm.system.task;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * 同步调用
 * @author DUHAOLIN
 * @date 2024/10/17
 */
@Slf4j
@Component
public class SyncTask {

    public static Random random = new Random();

    public void one() throws InterruptedException {
        commonTask("一");
    }
    


    public void two() throws InterruptedException {
        commonTask("二");
    }

    public void three() throws InterruptedException {
        commonTask("三");
    }

    public void commonTask(String s) throws InterruptedException {
        log.info("开始执行任务" + s);
        long startTime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long endTime = System.currentTimeMillis();
        log.info("完成任务" + s + ",耗时:" + (endTime - startTime) + "毫秒");
    }

}

第三步:测试类添加同步调用的测试方法

SystemApplicationTests.java

java 复制代码
@Slf4j
@SpringBootTest(classes = SystemApplication.class)
class SystemApplicationTests {
    
    @Resource
    private SyncTask syncTask;

    @Test
    public void syncTest() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        syncTask.one();
        syncTask.two();
        syncTask.three();
        long endTime = System.currentTimeMillis();
        log.info("任务总耗时:" + (endTime - startTime) + "毫秒");
    }
    
}

异步调用

第四步:添加异步调用业务逻辑

AsyncTask.java

java 复制代码
/**
 * 异步调用
 * @author DUHAOLIN
 * @date 2024/10/17
 */
@Slf4j
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async
    public void one() throws InterruptedException {
        commonTask("一");
    }

    @Async
    public void two() throws InterruptedException {
        commonTask("二");
    }

    @Async
    public void three() throws InterruptedException {
        commonTask("三");
    }

    public void commonTask(String s) throws InterruptedException {
        log.info("开始执行任务" + s);
        long startTime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long endTime = System.currentTimeMillis();
        log.info("完成任务" + s + ",耗时:" + (endTime - startTime) + "毫秒");
    }

}

第五步:测试类添加异步调用的测试方法

SystemApplicationTests.java

java 复制代码
@Resource
private AsyncTask asyncTask;

@Test
public void asyncTest() throws InterruptedException {
    long startTime = System.currentTimeMillis();
    asyncTask.one();
    asyncTask.two();
    asyncTask.three();
    long endTime = System.currentTimeMillis();
    log.info("任务总耗时:" + (endTime - startTime) + "毫秒");
}

异步回调(常用)

第六步:添加异步回调业务逻辑

AsyncCallBackTask.java

java 复制代码
package com.lm.system.task;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;

package com.lm.system.task;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.CompletableFuture;

/**
 * 异步回调
 * @author DUHAOLIN
 * @date 2024/10/17
 */
@Slf4j
@Component
public class AsyncCallBackTask {

    public static Random random = new Random();

    @Async
    public CompletableFuture<String> one() throws InterruptedException {
        log.info("开始执行任务一");
        long startTime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long endTime = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (endTime - startTime) + "毫秒");
        return CompletableFuture.completedFuture("任务一执行完成");
    }

    @Async
    public CompletableFuture<String> two() throws InterruptedException {
        log.info("开始执行任务二");
        long startTime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long endTime = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (endTime - startTime) + "毫秒");
        return CompletableFuture.completedFuture("任务二执行完成");
    }

    @Async
    public CompletableFuture<String> three() throws InterruptedException {
        log.info("开始执行任务三");
        long startTime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long endTime = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (endTime - startTime) + "毫秒");
        return CompletableFuture.completedFuture("任务三执行完成");
    }

}

第七步:测试类添加异步回调的测试方法

SystemApplicationTests.java

java 复制代码
@Resource
    private AsyncCallBackTask asyncCallBackTask;

    @Test
    public void asyncCallBackTaskTest() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        CompletableFuture<String> one = asyncCallBackTask.one();
        CompletableFuture<String> two = asyncCallBackTask.two();
        CompletableFuture<String> three = asyncCallBackTask.three();
        CompletableFuture.allOf(one, two, three).join();
        long endTime = System.currentTimeMillis();
        log.info("任务总耗时:" + (endTime - startTime) + "毫秒");
    }

效果图

同步调用

异步调用

执行完异步调用,只有任务的部分相关输出,任务的执行顺序也是乱序的。

异步回调

异步任务线程池配置

介绍

当我们用异步调用或异步回调进行并发操作时,加速了任务的执行效率,但是如果只是直接简单的创建来使用,可能会碰到一些问题和风险。当接口被频繁调用,异步任务创建的数量达到一定量级,可能会导致内存溢出,此时我们就需要对创建异步任务的操作,加上线程池的相关配置。

queueCapacity:缓冲队列的容量,默认为INT的最大值(2的31次方-1)

maxSize:允许的最大线程数,默认为INT的最大值(2的31次方-1)

具体配置

application.yml

yml 复制代码
spring:
  task:
    execution:
      pool:
        core-size: 2 #线程池创建时的初始化线程数,默认为8
        max-size: 5 #线程池的最大线程数,默认为int最大值
        queue-capacity: 10 #用来缓冲执行任务的队列,默认为int最大值
        keep-alive: 60s #线程终止前允许保持空闲的时间,默认为60s
        allow-core-thread-timeout: true #是否允许核心线程超时
      shutdown:
        await-termination: false #是否等待剩余任务完成后才关闭应用
        await-termination-period: #等待剩余任务完成的最大时间
      thread-name-prefix: task- #线程名的前缀,设置好了之后可以方便我们在日志中查看处理任务所在的线程池

application.Properties

properties 复制代码
spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-

效果图

再次执行异步回调方法,得到如下效果图。

当前配置的初始化线程数为2,最大线程数为5,缓存队列为10,只有当缓存队列满且当前线程数小于最大线程数时,才会申请新的线程来执行任务(如:缓存队列为11,初始化线程数为2,最大线程数为5)。

项目结构图

参考资料

Spring Boot 2.x基础教程:使用@Async实现异步调用【https://www.didispace.com/spring-boot-2/8-3-async-1.html】

Spring Boot 2.x基础教程:配置@Async异步任务的线程池【https://www.didispace.com/spring-boot-2/8-3-async-2.html】

相关推荐
计算机学长felix15 分钟前
基于SpringBoot的“交流互动系统”的设计与实现(源码+数据库+文档+PPT)
spring boot·毕业设计
.生产的驴16 分钟前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文18 分钟前
宠物管理系统:Dao层
java·开发语言·宠物
顽疲21 分钟前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
王ASC1 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web
撒呼呼1 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
是小崔啊1 小时前
开源轮子 - Apache Common
java·开源·apache
因我你好久不见1 小时前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg
程序员shen1616111 小时前
抖音短视频saas矩阵源码系统开发所需掌握的技术
java·前端·数据库·python·算法
Ling_suu1 小时前
SpringBoot3——Web开发
java·服务器·前端