springboot中使用async实现异步编程

news2025/4/8 5:40:25

目录

1.说明

2.实现原理

3.示例

4.总结


1.说明

@Async 是 Spring 框架提供的一个注解,用于标记方法为异步执行。被标记的方法将在调用时立即返回,而实际的方法执行将在单独的线程中进行。

@Async 注解有一个可选属性:指定要使用的特定线程池的 bean 名称

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
    /**
     * 指定要使用的执行器(线程池)的限定符值
     */
    String value() default "";
}

@Async 方法可以有以下返回类型:

  1. void:无返回值

  2. Future:返回 Future 对象,可用于获取异步执行结果

  3. CompletableFuture/ListenableFuture:更现代的异步结果处理方式

2.实现原理

基于 Spring AOP(面向切面编程)和任务执行器(TaskExecutor)框架。

3.示例

①启动类中开启异步支持

@EnableAsync  // 开启异步支持	
@SpringBootApplication	
public class Application {	
    public static void main(String[] args) {	
        SpringApplication.run(Application.class, args);	
    }	
}

EnableAsync有两个主要参数: mode及proxyTargetClass

@EnableAsync 通过 mode 属性来配置代理方式:

AdviceMode 枚举有两种选择:

  1. AdviceMode.PROXY (默认值) - 使用标准 Spring AOP 代理

    • 对接口使用 JDK 动态代理

    • 对类使用 CGLIB 代理

  2. AdviceMode.ASPECTJ - 使用 AspectJ 编织(weaving)

    • 需要额外的 AspectJ 依赖

    • 不需要接口也能代理

    • 可以代理更多方法类型(如 private 方法)

proxyTargetClass:指定使用CGLIB 代理还是JDK动态代理

proxyTargetClass = true 表示:

  • 强制使用 CGLIB 代理,即使目标类实现了接口

  • 代理目标类本身,而不是其实现的接口

  • 可以代理没有接口的类

默认行为(proxyTargetClass = false 或未指定)

  • 如果目标类实现了接口 → 使用 JDK 动态代理

  • 如果目标类没有实现接口 → 使用 CGLIB 代理

一般来说使用AdviceMode.PROXY及proxyTargetClass = true

②配置线程池

可以配置多个线程池

package com.example.demo1.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class ThreadPoolConfig {

    @Bean("executorPool1")
    public ThreadPoolTaskExecutor executorPool1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);// 核心线程数
        executor.setMaxPoolSize(10);// 最大线程数
        executor.setQueueCapacity(100);// 队列容量
        executor.setKeepAliveSeconds(60);// 空闲线程的存活时间
        executor.setThreadNamePrefix("demo1-pool-");// 线程名称前缀
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 拒绝策略,让调用者线程自己执行被拒绝的任务
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); //默认的拒绝策略,会抛出RejectedExecutionException异常
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 拒绝策略,直接丢弃任务,不抛出异常
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());// 拒绝策略,丢弃队列中最老的任务,尝试再次提交当前任务
        executor.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成
        executor.setAwaitTerminationSeconds(60);           // 等待终止的最长时间(秒)
        return executor;
    }

    @Bean("executorPool2")
    public ThreadPoolTaskExecutor executorPool2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);// 核心线程数
        executor.setMaxPoolSize(10);// 最大线程数
        executor.setQueueCapacity(100);// 队列容量
        executor.setKeepAliveSeconds(60);// 空闲线程的存活时间
        executor.setThreadNamePrefix("demo1-pool-");// 线程名称前缀
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 拒绝策略,让调用者线程自己执行被拒绝的任务
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); //默认的拒绝策略,会抛出RejectedExecutionException异常
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 拒绝策略,直接丢弃任务,不抛出异常
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());// 拒绝策略,丢弃队列中最老的任务,尝试再次提交当前任务
        executor.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成
        executor.setAwaitTerminationSeconds(60);           // 等待终止的最长时间(秒)
        return executor;
    }
}

 ③定义异步方法

package com.example.demo1.logic;

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

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;


@Service
public class AsyncLogic {

    @Async("executorPool1")
    public CompletableFuture<String> process1(String item) {
        CompletableFuture<String> future = new CompletableFuture<>();
        try {
            System.out.println("线程名称:" + Thread.currentThread().getName());
            Thread.sleep(1000);
            future.complete("Processed: " + item);
        } catch (Exception e) {
            e.printStackTrace();
            future.completeExceptionally(e);
        }
        return future;
    }

    @Async("executorPool2")
    public Future<String> process2(String item) {
        try {
            System.out.println("线程名称:" + Thread.currentThread().getName());
            Thread.sleep(1000);
            return AsyncResult.forValue("Processed: " + item);
        } catch (Exception e) {
            e.printStackTrace();
            return AsyncResult.forExecutionException(e);
        }
    }

    @Async("executorPool1")
    public void process3(String item) {
        try {
            System.out.println("线程名称:" + Thread.currentThread().getName());
            Thread.sleep(1000);
            System.out.println("执行");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

④调用异步方法

package com.example.demo1;

import com.example.demo1.logic.AsyncLogic;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.Async;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@SpringBootTest
public class AsyncTest {

    @Autowired
    private AsyncLogic asyncLogic;


    @Test
    public void test114() {
        System.out.println("调用异步方法前");
        CompletableFuture<String> completableFuture = asyncLogic.process1("item");

        Future<String> item = asyncLogic.process2("item");

        asyncLogic.process3("item");

        System.out.println("执行其他操作,不阻塞主线程");

        try {
            System.out.println("获取异步任务结果,阻塞主线程");
            String result = completableFuture.get();
            System.out.println("异步任务结果1:" + result);
            String s = item.get();
            System.out.println("异步任务结果2:" + s);
        } catch (Exception e) {
            System.out.println("捕捉到异常");
        }

        System.out.println("调用异步方法后");
    }

}

4.总结

1. 必须通过 Spring Bean 调用

❌ 错误:直接调用 this.asyncMethod()(同一类内调用不生效)。

✅ 正确:通过 @Autowired 注入自身或使用其他 Bean 调用。
2. 异常处理

默认静默吞异常,建议添加异常处理器:
异常时记录日志,并发送钉钉通知
3. 方法可见性

@Async 需作用于 public 方法,私有方法无效。
 

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

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

相关文章

【教程】MacBook 安装 VSCode 并连接远程服务器

目录 需求步骤问题处理 需求 在 Mac 上安装 VSCode&#xff0c;并连接跳板机和服务器。 步骤 Step1&#xff1a;从VSCode官网&#xff08;https://code.visualstudio.com/download&#xff09;下载安装包&#xff1a; Step2&#xff1a;下载完成之后&#xff0c;直接双击就能…

Unet网络的Pytorch实现和matlab实现

文章目录 一、Unet网络简介1.1 输入图像1.2 编码器部分&#xff08;Contracting Path&#xff09;1.3 解码器部分&#xff08;Expanding Path&#xff09;1.4 最后一层&#xff08;输出&#xff09;1.5 跳跃连接&#xff08;Skip Connections&#xff09; 二、Unet网络的Pytorc…

【合新通信】相控阵雷达RFoF方案的应用

一、相控阵雷达为何需要RFoF&#xff1f; 核心需求驱动 分布式部署&#xff1a;相控阵雷达&#xff08;AESA/PESA&#xff09;的T/R模块需分散布局&#xff08;如舰载雷达阵面、卫星载荷&#xff09;&#xff0c;传统同轴电缆导致重量和损耗剧增。高频段挑战&#xff1a;X/Ku/…

原理图输出网表及调入

一、输出网表操作步骤 &#xff08;1&#xff09;选中.dsn文件&#xff0c;选者N或进入tools下拉列表选择Creat Netlists &#xff08;2&#xff09;导出网表后的文件 二、网表的导入 &#xff08;1&#xff09;执行菜单命令“File-Import-Logic/netlist”&#xff0c;将原理…

TDengine JAVA 语言连接器

简介 本节简介 TDengine 最重要且使用最多的连接器, 本节内容是以教科书式方式列出对外提供的接口及功能及使用过程中要注意的技术细节&#xff0c;大家可以收藏起来做为今后开发 TDengine 的参考资料。 taos-jdbcdriver 是 TDengine 的官方 Java 语言连接器&#xff0c;Java…

【NLP 55、实践 ⑬ LoRA完成NER任务】

目录 一、数据文件 二、模型配置文件 config.py 三、数据加载文件 loader.py 1.导入文件和类的定义 2.初始化 3.数据加载方法 代码运行流程 4.文本编码 / 解码方法    ① encode_sentence()&#xff1a; ② decode()&#xff1a; 代码运行流程 ③ padding()&#xff1a; 代码…

【蓝桥杯】Python大学A组第十五届省赛

1.填空题 1.1.拼正方形 问题描述 小蓝正在玩拼图游戏,他有个的方块和个的方块,他需要从中挑出一些来拼出一个正方形。 比如用个和个的方块可以拼出一个的正方形;用个的方块可以拼出一个的正方形。 请问小蓝能拼成的最大的正方形的边长为多少。 import math # 2*2的个数 a =…

小球反弹(蓝桥杯C语言)

有一长方形&#xff0c;长为 343720343720 单位长度&#xff0c;宽为 233333233333 单位长度。在其内部左上角顶点有一小球 (无视其体积)&#xff0c;其初速度如图所示且保持运动速率不变&#xff0c;分解到长宽两个方向上的速率之比为 dx:dy15:17dx:dy15:17。小球碰到长方形的…

HarmonyOS-ArkUI Ability进阶系列-UIAbility与各类Context

UIAbility及相关类关系 一个模块编译的时候会出一个HAP包&#xff0c; 每一个HAP包在运行时都对应一个AbilityStage。 AbilityStage持有一个AbilityStageContext一个APP&#xff0c; 有时候会有很多个HAP包&#xff0c; 至少一个。 一个APP运行时&#xff0c;对应的是我们的App…

剑指Offer(数据结构与算法面试题精讲)C++版——day4

剑指Offer&#xff08;数据结构与算法面试题精讲&#xff09;C版——day4 题目一&#xff1a;和为k的子数组题目二&#xff1a;0和1个数相同的子数组题目三&#xff1a;左右两边子数组的和相等 题目一&#xff1a;和为k的子数组 结合前面着重阐述的双指针法这一经典的算法技巧&…

WebRTC技术简介及应用场景

写在前面 本文是参考稀土掘金的文章,整理得出,版权归原作者所有!参考链接请点击跳转 WebRTC&#xff08;Web Real-Time Communication&#xff09; 是一项开源技术&#xff0c;允许浏览器和移动应用直接进行实时音视频通信和数据传输&#xff0c;无需安装插件或第三方软件。它…

介绍几种创意登录页(含完整源码)

今天为大家收集了几种不同风格的登录页&#xff0c;搭配动态渐变背景&#xff0c;效果绝对惊艳&#xff01; CSS3实现动态渐变玻璃拟态登录页 一、开篇语 纯CSS实现当下最火的玻璃拟态(Morphism)风格登录页&#xff0c;搭配动态渐变背景&#xff0c;效果绝对惊艳&#xff01; …

Uni-app入门到精通:uni-app的基础组件

1、view view是容器组件&#xff0c;类似于HTML中的<div></div>标签&#xff0c;用于包裹各种元素内容&#xff0c;是页面布局常用的组件。view组件的属性如下 属性类型默认值说明hover-classStringnone指定按下去的样式类。当hover-class"none"时&…

大文件上传源码,支持单个大文件与多个大文件

大文件上传源码&#xff0c;支持单个大文件与多个大文件 Ⅰ 思路Ⅱ 具体代码前端--单个大文件前端--多个大文件前端接口后端 Ⅰ 思路 具体思路请参考我之前的文章&#xff0c;这里分享的是上传流程与源码 https://blog.csdn.net/sugerfle/article/details/130829022 Ⅱ 具体代码…

C语言--插入排序

插入排序&#xff1a;简单而高效的排序算法 在计算机科学中&#xff0c;排序是一种常见的操作&#xff0c;用于将一组数据按照特定的顺序排列。插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的工作原理类似于我们整理扑克牌的过程。…

L2-024 部落 #GPLT,并查集 C++

文章目录 题目解读输入格式输出格式 思路Ac Code参考 题目解读 我们认为朋友的朋友都算在一个部落里&#xff0c;于是要请你统计一下&#xff0c;在一个给定社区中&#xff0c;到底有多少个互不相交的部落&#xff1f;并且检查任意两个人是否属于同一个部落。 输入格式 第一…

在线记事本——支持Markdown

项目地址 https://github.com/Anyuersuper/CloudNotebook 百度网盘 通过网盘分享的文件&#xff1a;CloudNotebook-master.zip 链接: https://pan.baidu.com/s/1_Y--aBzNkKiFRIMHYmwPdA?pwdyuer 提取码: yuer &#x1f4dd; 云笔记 (Cloud Notebook) 云笔记是一个简洁、安全…

Day2:前端项目uniapp壁纸实战

先来做一个轮番图。 效果如下&#xff1a; common-style.css view,swiper,swiper-item{box-sizing: border-box; } index.vue <template><view class"homeLayout"><view class"banner"><swiper circular indicator-dots autoplay…

LeetCode Hot100 刷题笔记(3)—— 链表

目录 前言 1. 相交链表 2. 反转链表 3. 回文链表 4. 环形链表 5. 环形链表 II 6. 合并两个有序链表 7. 两数相加 8. 删除链表的倒数第 N 个结点 9. 两两交换链表中的节点 10. K 个一组翻转链表 11. 随机链表的复制 12. 排序链表 13. 合并 K 个升序链表 14. LRU 缓存 前言 一、…

状态机思想编程

1. LED流水灯的FPGA代码 在这个任务中&#xff0c;首先我们会使用状态机的思想来设计一个LED流水灯的控制逻辑。LED流水灯一般需要依次点亮不同的LED&#xff0c;并且循环播放。我们将其分为几个状态&#xff0c;每个状态控制一个或一组LED灯。 状态机设计 假设我们有8个LED…