当前位置:首页 > 技术分析 > 正文内容

阿里一面:为什么不建议直接使用Spring 中的@async注解?

ruisui883个月前 (02-14)技术分析13

在 Spring 开发中,@Async 注解看似方便地提供了异步执行的功能,但实际上直接使用却存在诸多问题。今天,我们就来深入探讨一下为什么不建议直接使用 @Async 注解,以及在实际开发中应该如何正确运用它。

直接使用 @Async 的潜在问题

  1. 异常处理难题

当被 @Async 注解的方法抛出异常时,异常可能无法像同步方法那样被直接捕获和处理。因为异步方法通常在不同线程中执行,异常难以传播到调用者线程。比如,一个异步方法执行中抛出RuntimeException,调用者可能无法及时察觉,导致问题难以被快速发现和解决。

同时,由于异步方法执行独立,很难为所有异步方法建立统一的异常处理机制,不同方法处理异常方式各异,增加了代码维护的难度。

  1. 调试困境

异步方法在不同线程中执行,使得调试变得极为复杂。开发者难以跟踪异步方法的执行流程,不知道方法何时开始、在哪个线程执行以及执行进度如何,给调试带来巨大挑战。

而且,由于异步执行的不确定性,一些问题可能难以重现。比如,一个异步方法在某些情况下出现问题,在其他情况却正常运行,这让问题的定位和解决变得更加困难。

  1. 系统资源影响

不加限制地使用 @Async 注解,可能会导致过多线程被创建。每个异步方法都在新线程中执行,在高并发情况下,会消耗大量系统资源。例如,大量异步方法同时被调用,系统可能创建大量线程,导致内存消耗增加、CPU 负载升高,甚至可能出现线程饥饿或死锁等问题。

管理大量异步线程也是复杂任务,要考虑线程的创建、销毁、调度等,若管理不当,会导致系统性能下降或出现故障。

  1. 缺乏控制和监控

异步方法执行顺序不确定,若多个异步方法存在依赖关系,很难确保它们按特定顺序执行。比如,一个异步方法可能需等待另一个异步方法结果才能继续执行,但由于异步特性,无法确定另一个方法何时完成,可能导致程序出错。

对于异步方法的执行状态也难以有效监控,无法确定方法是否已开始执行、正在执行或已完成执行,在需要实时监控任务执行状态的场景下,使用 @Async 注解不太合适。

@Async 注解底层实现

@Async 注解的实现基于 Spring 的异步执行框架。当一个方法被标注为 @Async 时,Spring 会在后台创建新线程或从线程池中获取线程来执行该方法。

Spring 默认使用ThreadPoolTaskExecutor作为异步执行器,它是基于线程池的任务执行器。当异步方法被调用,Spring 会将执行任务提交给ThreadPoolTaskExecutor,由它分配线程执行任务。

此外,Spring 还提供了SimpleAsyncTaskExecutor等其他异步执行器。如果直接使用 @Async 注解而不进行额外配置,Spring 就会直接使用SimpleAsyncTaskExecutor。它会为每个异步任务创建新线程,不像ThreadPoolTaskExecutor使用线程池,在高并发情况下可能导致过多线程被创建,消耗大量系统资源。

在底层实现中,Spring 还会对异步方法的调用进行处理,如封装方法的参数和返回值,以便在不同线程中传递和处理。同时,也会对异步方法的异常进行处理,将异常封装后传递给调用者,以便捕获和处理。

实际开发中如何正确使用 @Async 注解

  1. 配置合适的异步执行器

在实际开发中,不能直接使用默认的异步执行器,应根据项目实际需求配置合适的异步执行器。比如,可以使用ThreadPoolTaskExecutor管理线程池,设置合适的线程池大小、队列长度等参数,避免过多线程被创建,同时确保任务能及时得到处理。配置示例:

     @Configuration
     public class AsyncConfig {
         @Bean
         public Executor asyncExecutor() {
             ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
             executor.setCorePoolSize(10);
             executor.setMaxPoolSize(20);
             executor.setQueueCapacity(100);
             executor.setThreadNamePrefix("AsyncTask-");
             executor.initialize();
             return executor;
         }
     }
  1. 处理异常

对于异步方法中可能出现的异常,应建立统一的异常处理机制。可以使用 @Async 注解的errorHandler属性指定异常处理方法,或者在调用异步方法的地方进行异常处理。示例:

     @Async(errorHandler = "asyncExceptionHandler")
     public void asyncMethod() {
         // 异步方法的逻辑
     }

     public void asyncExceptionHandler(Throwable t) {
         // 异常处理逻辑
     }
  1. 考虑执行顺序和依赖关系

若异步方法之间存在依赖关系,应采取适当措施确保它们按正确顺序执行。可以使用CountDownLatch、Future等机制等待异步方法完成,或者将依赖的任务合并到一个方法中执行。示例:

     @Async
     public Future asyncMethod1() {
         // 异步方法 1 的逻辑
         return new AsyncResult<>("result1");
     }

     @Async
     public Future asyncMethod2(Future future1) {
         // 等待异步方法 1 完成
         try {
             String result1 = future1.get();
             // 异步方法 2 的逻辑
             return new AsyncResult<>("result2");
         } catch (InterruptedException | ExecutionException e) {
             // 异常处理逻辑
             return new AsyncResult<>("error");
         }
     }
  1. 监控异步任务

在实际开发中,可以使用一些工具监控异步任务的执行状态。例如,可以使用 Spring 的TaskExecutor接口的实现类提供的方法获取任务执行信息,或者使用第三方监控工具实时监控异步任务执行情况。示例:

     @Async
     public void asyncMethod() {
         // 异步方法的逻辑
         System.out.println("Task started.");
         // 模拟任务执行时间
         try {
             Thread.sleep(2000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         System.out.println("Task completed.");
     }

     public void monitorAsyncTasks() {
         ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) asyncExecutor();
         System.out.println("Active tasks: " + executor.getActiveCount());
         System.out.println("Pending tasks: " + executor.getQueue().size());
     }

在 Spring 开发中,虽然 @Async 注解提供了异步执行的强大功能,但直接使用可能会带来一系列问题。我们需要根据实际情况进行合理配置和处理,才能充分发挥其优势,确保异步任务正确、高效地执行。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1960.html

标签: aysnc
分享给朋友:

“阿里一面:为什么不建议直接使用Spring 中的@async注解?” 的相关文章

一套智能停车场收费管理系统设计方案,拓扑图VISIO格式

大家好,我是薛哥。最近VIP会员群的读者咨询停车场管理系统的规划设计方案,今天分享一个模板素材,主要里面的拓扑图可以编辑的,VISIO格式,建议收藏备用。此套完整的Word方案,VIP会员下载!智能停车场收费管理子系统1、系统概述本次停车场管理系统设计纯车牌识别系统,并可在合适的位置设置中央收费点,...

Vue3,父组件子组件传值,provide(提供)和inject(注入)传值

父组件向子组件传值父子组件传递数据时,通常使用的是props和emit,父向子传递使用props,子向父传递使用emit。子组件接收3种方式// 1、简单接收 props:["title","isShow"], // 2、接收的同时对数据类型进行限制 props:{...

程序员开发必会之git常用命令,git配置、拉取、提交、分支管理

整理日常开发过程中经常使用的git命令![送心]git配置SSH刚进入项目开发中,我们首先需要配置git的config、配置SSH方式拉取代码,以后就免输入账号密码了!# 按顺序执行 git config --global user.name "自己的账号" git config -...

Excel中的FILTER函数详细介绍及使用示例

在Excel中处理大量数据时,经常需要根据特定条件筛选出符合条件的数据行或列。这正是Excel的FILTER函数发挥作用的地方。FILTER函数是Excel中一个非常强大的工具,它可以基于一个或多个条件动态地过滤数据,使数据分析和报告制作变得更加高效和准确。本文将详细介绍FILTER函数的用法,并提...

Python中的11 种数组算法

1. 创建数组 创建数组意味着留出一个连续的内存块来存储相同类型的元素。在大多数语言中,您可以在创建数组时指定数组的大小。假设您正在书架上整理一组书籍,并且您需要为正好 10 本书预留空间。功能架上的每个空间都对应于数组中的一个索引。# Example in Python arr = [1, 2,...

从 Vue2.0 到 React17——React 开发入门

作者:佚名来源:前端大全前言找工作时发现有一些公司是以React作为技术栈的,而且薪资待遇都不错,为了增加生存的筹码,所以还是得去学一下React,增加一项求生技能。因为我用Vue2.0开发项目已经四年了,故用Vue2.0开发项目的思路来学习React。前端项目是由一个个页面组成的,对于Vue来说,...