SpringBoot异步方法支持注解@Async应用

news2024/10/5 23:27:28

SpringBoot异步方法支持注解@Async应用

1.为什么需要异步方法?

合理使用异步方法可以有效的提高执行效率

同步执行(同在一个线程中):图片

异步执行(开启额外线程来执行):

图片

2.SpringBoot中的异步方法支持

在SpringBoot中并不需要我们自己去创建维护线程或者线程池来异步的执行方法, SpringBoot已经提供了异步方法支持注解.

@EnableAsync // 使用异步方法时需要提前开启(在启动类上或配置类上)
@Async // 被async注解修饰的方法由SpringBoot默认线程池(SimpleAsyncTaskExecutor)执行

service层:

@Service
public class ArticleServiceImpl {

    // 查询文章
    public String selectArticle() {
        //  模拟文章查询操作
        System.out.println("查询任务线程"+Thread.currentThread().getName());
        return "文章详情";
    }

    // 文章阅读量+1
    @Async
    public void updateReadCount() {
        try {
            // 模拟耗时操作
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("更新任务线程"+Thread.currentThread().getName());
    }
}

controller层:

@RestController
public class AsyncTestController {

    @Autowired
    private ArticleServiceImpl articleService;

    /**
     * 模拟获取文章后阅读量+1
     */
    @GetMapping("/article")
    public String getArticle() {
        long start = System.currentTimeMillis();
        // 查询文章
        String article = articleService.selectArticle();
        // 阅读量+1
        articleService.updateReadCount();
        long end = System.currentTimeMillis();
        System.out.println("文章阅读业务执行完毕,执行共计耗时:"+(end-start));
        return article;
    }

}

测试结果: 我们可以感受到接口响应速度大大提升, 而且从日志中key看到两个执行任务是在不同的线程中执行的

查询任务线程http-nio-8800-exec-3
文章阅读业务执行完毕,执行共计耗时:56
更新任务线程task-1

3.自定义线程池执行异步方法

SpringBoot为我们默认提供了线程池(SimpleAsyncTaskExecutor)来执行我们的异步方法, 我们也可以自定义自己的线程池.

第一步配置自定义线程池

@EnableAsync // 开启多线程, 项目启动时自动创建
@Configuration
public class AsyncConfig {
    @Bean("customExecutor")
    public ThreadPoolTaskExecutor asyncOperationExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 设置队列大小
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置线程名前缀+分组名称
        executor.setThreadNamePrefix("AsyncOperationThread-");
        executor.setThreadGroupName("AsyncOperationGroup");
        // 所有任务结束后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        executor.initialize();
        return executor;
    }
}

第二步, 在@Async注解上指定执行的线程池即可

// 文章阅读量+1
@Async("customExecutor")
public void updateReadCount() {
    // TODO 模拟耗时操作
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("更新文章阅读量线程"+Thread.currentThread().getName());
}

测试结果:

查询任务线程http-nio-8800-exec-1
文章阅读业务执行完毕,执行共计耗时:17
更新任务线程AsyncOperationThread-1

4.如何捕获(无返回值的)异步方法中的异常

以实现AsyncConfigurer接口的getAsyncExecutor方法和getAsyncUncaughtExceptionHandler方法改造配置类

自定义异常处理类CustomAsyncExceptionHandler

@EnableAsync // 开启多线程, 项目启动时自动创建
@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 设置队列大小
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置线程名前缀+分组名称
        executor.setThreadNamePrefix("AsyncOperationThread-");
        executor.setThreadGroupName("AsyncOperationGroup");
        // 所有任务结束后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
        System.out.println("异常捕获---------------------------------");
        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
        System.out.println("异常捕获---------------------------------");
    }

}

测试结果:

查询任务线程http-nio-8800-exec-1
文章阅读业务执行完毕,执行共计耗时:20
异常捕获---------------------------------
Exception message - / by zero
Method name - updateReadCount
异常捕获---------------------------------

5.如何获取(有返回值)异步方法的返回值

使用Future类及其子类来接收异步方法返回值

注意:

  • 无返回值的异步方法抛出异常不会影响Controller的主要业务逻辑
  • 有返回值的异步方法抛出异常会影响Controller的主要业务逻辑
// 异步方法---------------------------------------------------------------------
@Async
public CompletableFuture<Integer> updateReadCountHasResult() {
    try {
        // 模拟耗时操作
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("更新文章阅读量线程"+Thread.currentThread().getName());
    return CompletableFuture.completedFuture(100 + 1);
}

// Controller调用---------------------------------------------------------------------
@GetMapping("/article")
public String getArticle() throws ExecutionException, InterruptedException {
    // 查询文章
    String article = articleService.selectArticle();
    // 阅读量+1
    CompletableFuture<Integer> future = articleService.updateReadCountHasResult();
    int count = 0;
    // 循环等待异步请求结果
    while (true) {
        if(future.isCancelled()) {
            System.out.println("异步任务取消");
            break;
        }
        if (future.isDone()) {
            count = future.get();
            System.out.println(count);
            break;
        }
    }
    System.out.println("文章阅读业务执行完毕");
    return article + count;
}

6.常见失效场景

  1. 主启动类或者配置类没有添加@EnableAsync注解

  2. A方法调用被@Async注解修饰的B方法

@RestController
public class UserController {
 
    @Resource
    private UserService userService;
 
    @RequestMapping("/getAll")
    public void getUsers(){
        System.out.println("业务开始");
        test();
        System.out.println("业务结束");
    }
 
    @Async
    public void test(){
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. 被@Async注解修饰的方法必须不可以是static和private,必须为public

  2. 需要通过@Autowired或@Resource进行注入,不可手动new,基于SpringAOP实现

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

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

相关文章

RTSP/Onvif协议安防视频平台EasyNVR录像模式自定义操作

TSINGSEE青犀视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif流媒体协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。在智慧安防等视频监控场景中&#xff0c;EasyNVR可提供视频实时监控直播…

【通用消息通知服务】0x3 - 发送我们第一条消息(Websocket)

【通用消息通知服务】0x3 - 发送我们第一条消息 项目地址: A generic message notification system[Github] 实现接收/发送Websocket消息 Websocket Connection Pool import asyncio from asyncio.queues import Queue from asyncio.queues import QueueEmpty from contextli…

Triplet Fingerprinting(三元组网站指纹攻击)

文章信息 论文题目&#xff1a;《Triplet Fingerprinting: More Practical and Portable Website Fingerprinting with N-shot Learning》 期刊&#xff08;会议&#xff09;&#xff1a;Proceedings of the 2019 ACM SIGSAC Conference on Computer and Communications Secur…

【Java 中级】一文精通 Spring MVC - 上传(十)

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

LeetCode第11~15题解

CONTENTS LeetCode 11. 盛最多水的容器&#xff08;中等&#xff09;LeetCode 12. 整数转罗马数字&#xff08;中等&#xff09;LeetCode 13. 罗马数字转整数&#xff08;简单&#xff09; LeetCode 11. 盛最多水的容器&#xff08;中等&#xff09; 【题目描述】 给定一个长…

广州华锐互动:VR垃圾分类虚拟科普系统让学习过程更加丰富有趣

在我们的日常生活中&#xff0c;垃圾分类已成为一项重要的公民责任。然而&#xff0c;由于缺乏对垃圾分类的深入理解和相关知识&#xff0c;许多人在实践中往往感到困惑和挫败。为了解决这个问题&#xff0c;一种创新的解决方案应运而生&#xff1a;垃圾分类VR虚拟仿真教学系统…

ESP32使用Arduino读写SD卡

背景 esp32屏幕包含一个sd卡接口&#xff0c;通过SPI线连接&#xff0c;需要对插入改SD接口中的TF卡进行读写&#xff0c;通过arduino平台实现。 代码中HSPI指ESP32的SPI2; 在Master模式下&#xff0c;SPID是MOSI/data out, SPIQ是MISO/data in&#xff1a; 代码 #include…

【C语言】指针 和 数组 笔试题详解

目录 一、数组 1.一维数组 2.字符数组 3.二维数组 二、指针 笔试题1 笔试题2 笔试题3 笔试题4 笔试题5 笔试题6 笔试题7 笔试题8&#xff08;有难度&#xff09;【看明白会有质的收获】 在这里我们需要先了解数组名的意义 sizeof(数组名) &#xff0c;这里的数组名表示…

7年经验之谈 —— 如何高效的开展app的性能测试

APP性能测试是什么 从网上查了一下&#xff0c;貌似也没什么特别的定义&#xff0c;我这边根据自己的经验给出一个自己的定义&#xff0c;如有巧合纯属雷同。 客户端性能测试就是&#xff0c;从业务和用户的角度出发&#xff0c;设计合理且有效的性能测试场景&#xff0c;制定…

代码随想录Day_48打卡

①、打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房…

Yolov1原理详细解读及实战(一)理论篇

什么是Yolov1? Yolo(You Only Look Once)是一种one-stage目标检测算法&#xff0c;即仅需要 “看” 一次就可以识别出图片中物体的class类别和位置。作为one-stage的开山鼻祖&#xff0c;YOLOv1以其简洁的网络结构和GPU上的实时检测速度而一鸣惊人&#xff0c;打破了R-CNN的“…

Linux centos7 bash编程(break和continue)

在学习shell知识时&#xff0c;简单编程要从格式入手。 首先学习好单行注释和多行注释。 先学习简单整数的打印输出&#xff0c;主要学习echo命令&#xff0c;学习选项-e -n的使用。 下面的练习是常用的两个分支程序&#xff1a;break和continue。 #!/bin/bash # 这是单行注…

极氪汽车的云资源治理细探

作者&#xff1a;极氪汽车吴超 前言 2021 年&#xff0c;极氪 001 迅速崭露头角&#xff0c;仅用 110 天便创下了首款车型交付量“最快破万”的纪录。2022 年 11 月&#xff0c;极氪 009 在短短 76 天内便率先完成了首批交付&#xff0c;刷新了中国豪华纯电品牌交付速度的纪录…

算法通过村第四关-栈白银笔记|括号问题

文章目录 前言1. 括号匹配问题2. 最小栈问题3. 最大栈 总结 前言 提示&#xff1a;如果让我送给年轻人四个字&#xff0c;就是&#xff1a;量力而行。 量力而行不会失眠&#xff0c;不会啃老&#xff0c;不会为各种考试焦虑。顺其自然活得轻松。其实&#xff0c;量力而行最易大…

数据库集群的简单了解

Update 关于操作的日志 1.0 redo log 读一次写一次 一共2次, 不安全 注意redo log是顺写 而file是随机 所以Mysql做出类似HDFS的操作 行为日志和数据分离&#xff0c;但是不同的是&#xff0c;Mysql在内存中操作修改&#xff0c;如果不出事故&#xff0c;由内存中的行为来直接…

Yolov1原理详细解读及实战(二)实战篇

在Yolov1原理详细解读及实战&#xff08;一&#xff09;理论篇 中&#xff0c;我们对Yolov1网络结构、算法流程、训练及推理原理进行了详细剖析&#xff0c;本章进入实战环节&#xff0c;话不多说&#xff0c;马上开始&#xff01; 环境 vscodeWSL:Ubuntu 18.04python 3.9.7 …

wireshark过滤器的使用

目录 wiresharkwireshark的基本使用wireshark过滤器的区别 抓包案例 wireshark wireshark的基本使用 抓包采用 wireshark&#xff0c;提取特征时&#xff0c;要对 session 进行过滤&#xff0c;找到关键的stream&#xff0c;这里总结了 wireshark 过滤的基本语法&#xff0c;…

外卖订餐系统源码:数字化时代餐饮服务的创新之道

在如今快节奏的生活中&#xff0c;外卖订餐系统源码正成为餐饮业界的一股创新浪潮。它为餐厅和创业者提供了一个数字化的平台&#xff0c;使订餐与配送更加便捷、高效。本文将为您展示如何使用外卖订餐系统源码创建一个简单但功能强大的订餐平台。 # 导入必要的模块 import d…

离线竞价功能说明及设置

为了更加方便广大用户不再熬夜竞价&#xff0c;西部数码推出了离线竞价功能&#xff0c;现已正式上线&#xff0c;欢迎大家使用反馈。 1、离线竟价功能说明 当您拥有域名的出价权限时&#xff0c;您可在 【我参与的竞价】或【我出价的域名】列表选中域名开启离线竟价。 设置…