Springboot中使用@Async注解7大失效场景及解决方案

news2024/11/16 19:58:34

文章目录

  • 前言
  • 一、配置类未启用异步支持
  • 二、线程池未正确配置
  • 三、Spring代理未生效
  • 四、方法不是 public 的
  • 五、调用者与被调用者在同一个类中
  • 六、异常处理不当
  • 七、使用 @Transactional 与 @Async 同时注解方法,导致事务失效
  • 总结


前言

    在Spring Boot中,@Async注解就像一把瑞士军刀,能帮你轻松处理那些耗时的任务,让主线程可以继续忙别的事儿。再强大的工具,如果使用不好依然会出现问题。为了避免这些坑,咱们得深入了解下@Async是怎么工作的,还要知道怎么用才能不出问题。


一、配置类未启用异步支持

    如果配置类中没有启用异步支持,即没有使用 @EnableAsync 注解,那么 @Async 注解同样不会生效。

// 没有使用 @EnableAsync 注解,因此不会启用异步支持  
@Configuration  
public class AsyncConfig {  
    // ... 其他配置 ...  
}  

@Service  
public class MyService {  

    @Async  
    public void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  
}

解决方案:

@Configuration
@EnableAsync
public class AsyncConfig {  
    // ... 其他配置 ...  
}

二、线程池未正确配置

    在使用 @Async 注解时,如果没有正确配置线程池,可能会遇到异步任务没有按预期执行的情况。
    比如:线程池被配置为只有一个线程,且该线程一直被占用,那么新的异步任务就无法执行。

@Configuration  
@EnableAsync  
public class AsyncConfig implements AsyncConfigurer {  

    @Override  
    public Executor getAsyncExecutor() {  
        // 创建一个只有一个线程的线程池,这会导致并发问题  
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
        executor.setCorePoolSize(1);  
        executor.setMaxPoolSize(1);  
        executor.setQueueCapacity(10);  
        executor.setThreadNamePrefix("Async-");  
        executor.initialize();  
        return executor;  
    }  

    // ... 其他配置 ...  
}

@Service  
public class MyService {  

    @Async  
    public void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  
}

解决方案
    正确配置线程池:确保线程池配置合理,能够处理预期的并发任务量

三、Spring代理未生效

    如果通过 new 关键字直接创建了服务类的实例,而不是通过 Spring 容器来获取,那么 Spring 的 AOP 代理将不会生效,导致 @Async 注解无效。

@Service  
public class MyService {  

    @Async  
    public void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  
}  

public class SomeNonSpringClass {  

    public void someMethod() {  
        MyService myService = new MyService(); // 直接通过 new 创建 MyService 实例,不会经过 Spring 代理  
        myService.asyncMethod(); // 这里 @Async 不会生效  
    }  
}

解决方案
    合理利用依赖注入:始终通过 Spring 容器来获取服务类的实例,而不是直接通过 new 关键字创建

@Service  
public class MyService {  
    @Async  
    public void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  
}  
@Service
public class SomeNonSpringClass {  
    @Autowired  
    private MyService myService;  
    public void someMethod() {   
        myService.asyncMethod(); // 这里 @Async 会生效  
    }  
}

四、方法不是 public 的

    @Async 注解的方法必须是 public 的,否则不会被 Spring AOP 代理捕获,导致异步执行不生效。

@Service  
public class MyService {  

    @Async // 但这个方法不是 public 的,所以 @Async 不会生效  
    protected void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  

    public void callAsyncMethod() {  
        asyncMethod(); // 直接调用,但由于 asyncMethod 不是 public 的,因此不会异步执行  
    }  
}

解决方案
    确保异步方法是 public 的

五、调用者与被调用者在同一个类中

    当调用 @Async 注解的方法的类和被调用的方法在同一个类中时,@Async 注解不会生效。

    因为 Spring 的 AOP 代理是基于接口的,对于同一个类中的方法调用,不会经过代理,因此 @Async 注解不会被处理。

@Service  
public class MyService {  

    @Async  
    public void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  

    public void callAsyncMethod() {  
        asyncMethod(); // 直接调用,不会异步执行  
    }  
}

解决方案:

  1. 确保异步方法和调用它的方法不在同一个类中。可以将异步方法提取到一个单独的 Service 中,并在需要的地方注入这个 Service。
  2. 确保异步方法的执行类(即包含 @Async 注解方法的类)被 Spring 容器管理,比如通过 @Service、@Component 等注解标注
//一定使用@Service、@Component 等注解标注,确保执行类被Spring管理,因为异步线程是通过动态代理实现的
@Service
public class AsyncService {
    @Async  
    public void asyncMethod() {  
        // 模拟耗时操作  
        try {  
            Thread.sleep(5000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Async method executed.");  
    }  
}

六、异常处理不当

    如果在异步方法中抛出了异常,并且没有妥善处理,那么这个异常可能会导致任务失败,而调用者可能无法感知到异常的发生。

@Service  
public class MyService {  

    @Async  
    public void asyncMethod() {  
        // 模拟一个可能会抛出异常的耗时操作  
        throw new RuntimeException("Async method exception");  
    }  
}
// 调用者  
@Service  
public class CallerService {  

    @Autowired  
    private MyService myService;  

    public void callAsyncMethod() {  
        myService.asyncMethod(); // 调用异步方法,但如果该方法抛出异常,调用者不会立即感知到  
    }  
}

解决方案
    合理处理异常:在异步方法中妥善处理异常,可以通过 Future 对象来捕获异步任务执行过程中抛出的异常。

七、使用 @Transactional 与 @Async 同时注解方法,导致事务失效

    在同一个方法上同时使用 @Transactional 和 @Async 注解可能会导致问题。

    由于 @Async 会导致方法在一个新的线程中执行,而 @Transactional 通常需要在一个由 Spring 管理的事务代理中执行,这两个注解的结合使用可能会导致事务管理失效或行为不可预测。

    此种场景不会导致@Async注解失效,但是会导致@Transactional注解失效,也就是事务失效。

@Service  
public class MyService {  

    @Autowired  
    private MyRepository myRepository;  

    // 错误的用法:同时使用了 @Transactional 和 @Async  
    @Transactional  
    @Async  
    public void asyncTransactionalMethod() {  
        // 模拟一个数据库操作  
        myRepository.save(new MyEntity());  

        // 模拟可能抛出异常的代码  
        if (true) {  
            throw new RuntimeException("Database operation failed!");  
        }  
    }  
}  

@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // ...  
}  

@Entity  
public class MyEntity {  
    // ... 实体类的属性和映射 ...  
}

    上面的代码,在抛出异常的时候,我们期望的是回滚前面的数据库保存操作,但是因为事务失效,会导致错误数据成功保存进数据库。

解决方案
    正确配置事务,比如单独提取事务执行的逻辑到一个新的Service里,事务执行方法单独使用@Transactional标识

@Service  
public class MyService {  

    @Autowired  
    private MyTransactionalService myTransactionalService;  

    @Autowired  
    private AsyncExecutor asyncExecutor;  

    public void callAsyncTransactionalMethod() {  
        // 在事务中执行数据库操作  
        MyEntity entity = myTransactionalService.transactionalMethod();  

        // 异步执行其他操作  
        asyncExecutor.execute(() -> {  
            // 这里执行不需要事务管理的异步操作  
            // ...  
        });  
    }  
}  

@Service  
public class MyTransactionalService {  

    @Autowired  
    private MyRepository myRepository;  

    @Transactional  
    public MyEntity transactionalMethod() {  
        // 在事务中执行数据库操作  
        return myRepository.save(new MyEntity());  
    }  
}  

@Component  
public class AsyncExecutor {  

    @Async  
    public void execute(Runnable task) {  
        task.run();  
    }  
}

总结

    这里面,绝大多数人会遇到的坑点主要会集中在没有配置自定义线程池、异步方法在同一个类中调用、事务不起作用这几个问题上。 所以,推荐还是专门定义一个AsyncService,将异步方法都写在里面,需要使用的时候,就在其他类将其注入即可。

“笑对人生,智慧同行!博客新文出炉,微信订阅号更新更实时,等你笑纳~”

在这里插入图片描述

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

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

相关文章

DP(2) | Java | LeetCode 62, 63, 343, 96 做题总结(96 未完)

62.不同路径 我的代码&#xff08;报错&#xff09; 写的过程中感到很迷惑的点&#xff1a;①二维数组和这道题目的对应弄不清除&#xff0c;m n的初始化 是 dp[m][n] 还是 dp[n][m] ② class Solution {public int uniquePaths(int m, int n) {int[][]dp new int[m1][n1];d…

LabVIEW电子水泵性能测试平台

开发了一种车用电子水泵性能测试平台&#xff0c;该平台以工控机为载体&#xff0c;利用LabVIEW开发上位机软件&#xff0c;采用PLC控制阀门和水泵等电气元件&#xff0c;通过RS485进行数据采集并传输到上位机。通过上位机与下位机的协同控制&#xff0c;实现了数据交互处理和性…

关于力反馈设备应用方向的探讨

力反馈是在虚拟现实 (VR)等模拟环境中通过机动运动或阻力模拟真实世界的物理触觉。大多数人都是通过视频游戏控制器&#xff08;如方向盘或踏板&#xff09;和其他设备&#xff08;如飞行模拟器操纵杆&#xff09;来了解力反馈效果。但我们都知道该技术的用途远不止于游戏。 触…

ENSP中OSPF配置

题目 划分网段&#xff0c;配置ip OSPF配置按照区域划分&#xff0c;这个网段也要按照区域个数划分&#xff0c;如这一题&#xff0c;分成两个区域&#xff0c;所以将192.168.1.0/24划分先为两个网段&#xff0c;然后在具体的划分区域中的网段。 以交换机为中心的三条线属于一…

探索AI人才培养新范式,合合信息与同济大学软件学院签署产教融合人才培养协议

随着科学技术的发展&#xff0c;促进人工智能产业与高校人才培养相融合&#xff0c;正成为业界关注的焦点。7月3日&#xff0c;上海合合信息科技股份有限公司&#xff08;以下简称&#xff1a;合合信息&#xff09;与同济大学软件学院“产教融合人才培养签约暨创新实践基地”揭…

模式物种葡萄基因组(T2T)--文献精读29

The complete reference genome for grapevine (Vitis vinifera L.) genetics and breeding 葡萄&#xff08;Vitis vinifera L.&#xff09;遗传学和育种的完整参考基因组 摘要 葡萄是全球最具经济重要性的作物之一。然而&#xff0c;以往版本的葡萄参考基因组通常由成千上万…

Qt/C++项目积累: 2.主机监控器 - 2.2 历史功能实现

修订历史&#xff1a; 20240711&#xff1a;初始表设计&#xff0c;采用sqlite 正文&#xff1a; 关于历史数据存储&#xff0c;考虑的是用数据库来完成&#xff0c;目前考虑使用Sqlite和mysql&#xff0c;先用sqlite来实现&#xff0c;设计表过程如下&#xff1a; 机器总览…

【栈和队列OJ题】

栈和队列OJ题 文章目录 栈和队列OJ题1. 用队列实现栈2. 用栈实现队列3. 括号匹配问题4. 循环队列 1. 用队列实现栈 OJ链接&#xff1a;225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 好的&#xff0c;我们一起来看一下题目&#xff0c;题目是这样说的 思路&…

IDEA实现热部署

什么是热部署&#xff1f; 热部署&#xff08;Hot Deployment&#xff09;是指在应用程序运行过程中&#xff0c;无需停止整个应用程序或重新启动服务器&#xff0c;就能够部署新的代码、资源或配置文件&#xff0c;使其立即生效。这种部署方式有助于提高开发效率和系统的可用性…

这些免费看电视的直播软件,还能免费追剧的app,需要的收藏!

想看中央台和地方卫视的电视直播app有什么呢&#xff1f;支持手机和智能电视的电视直播软件有哪些&#xff1f;今天要跟大家聊聊那些让人眼前一亮的电视直播软件&#xff0c;特别是2024年还能免费看电视直播的神器&#xff0c;让家里的老人也能享受到精彩的电视节目&#xff0c…

无线领夹麦克风哪个牌子好,热门无线麦克风十大排名推荐

​在这个信息爆炸的时代&#xff0c;声音的传递变得愈发重要。无论是直播中的激情解说&#xff0c;还是视频创作中的精彩对白&#xff0c;亦或是远程会议中的清晰发言&#xff0c;一款出色的无线领夹麦克风都能成为你的得力助手。今天&#xff0c;就让我们一同探索多款备受瞩目…

【测开能力提升-fastapi框架】fastapi模版引擎简单使用

1.6 通过模版引擎返回HTM页面 import uvicorn from fastapi import FastAPI, Request from fastapi.templating import Jinja2Templatesapp FastAPI()# 初始化模版引擎存放位置 templates Jinja2Templates(directory"templates")app.get("/") async def…

在Office里面无缝使用任何一家AI大模型,免费的!

昨天一个朋友说他在Word里面&#xff0c;用了一个插件&#xff0c;可以在右侧和AI对话&#xff0c;然后把AI生成的内容载入到左边的文档中。 我当时心理的想法&#xff1a;我这是穿越了吗&#xff1f;这不是我去年2月就实现&#xff0c;然后又扔掉的功能吗&#xff1f; 是的&a…

国漫推荐09

剧场版 1.龙之谷 《龙之谷&#xff1a;破晓奇兵》2014年7月31日 《龙之谷2精灵王座》2016年8月19日 2.星游记 《星游记之风暴法米拉》2017年8月11日 《星游记之风暴法米拉2》2020年3月28日 《星游记之冲出地球》2022-08-14 3.《大鱼海棠》2016年7月8日 4.《哪吒之魔童降世》2…

大数据如何推动工业数字化发展?

随着工业领域的深刻变革&#xff0c;数字化成为了驱动行业前行的核心力量。在这一转变中&#xff0c;大数据扮演着不可或缺的角色。它不仅为企业提供了洞察市场趋势、消费者行为等关键信息的窗口&#xff0c;还为企业优化生产流程、提升产品质量以及推动创新提供了强有力的支持…

前端-Cookie篇

文章目录 一、由来什么是Cookie&#xff1f;特点Cookie的类型 二、原理三、Cookie生成机制客户端设置案例 四、属性五、缺陷最后分享一段自己工作中封装的一些关于cookie的公众方法✒️总结 前端Cookie是Web开发中非常重要的一部分&#xff0c;它是服务器发送到用户浏览器并保存…

分享一款嵌入式开源LED指示灯控制代码框架cotLed

一、工程简介 cotLed是一款轻量级的LED控制软件框架&#xff0c;可以十分方便地控制及自定义LED的各种状态&#xff0c;移植方便&#xff0c;无需修改&#xff0c;只需要在初始化时实现单片机硬件GPIO初始化&#xff0c;同时为框架接口提供GPIO写函数即可。 框架代码工程地址&a…

C++:重定义

派生类和基类的同名成员问题 派生类中再实现一个基类中的方法会怎样 (1)代码实验&#xff1a;派生类和基类中各自实现一个内容不同但函数原型完全相同的方法&#xff0c;会怎么样 (2)结论&#xff1a;基类对象调用的是基类的方法&#xff0c;派生类对象调用执行的是派生类中重…

Vue3 + Echarts堆叠折线图的tooltip不显示问题

问题介绍 使用Echarts在Vue3Vite项目中绘制堆叠折线图的的时候&#xff0c;tooltip总是不显示&#xff0c;经过很长时间的排查和修改&#xff0c;最后发现是在使用上有错误导致的。 错误图片展示 问题原因 由于Vue3底层使用proxy代理创建示例&#xff0c;使用其创建出来的实…

甘蔗基因组--文献精读30

A chromosomal-scale genome assembly of modern cultivated hybrid sugarcane provides insights into origination and evolution 现代栽培杂交甘蔗的染色体级基因组组装提供了起源和进化的洞见&#xff0c;确实甘蔗好几个基因组了~ 摘要 甘蔗是一种具有重要经济和工业价值…