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

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

ruisui884个月前 (02-14)技术分析20

在 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注解?” 的相关文章

gitlab 分支保护设置

一、功能描述代码管理中管理,我们把稳定的分支设置为保护,可以防止其他人员误操作(例如删除,合并,推送代码等)。二、Gitlab配置步骤1 点击项目Repository标签2.点击Expand标签3.配置如下:默认master是被保护的,而且只有维护人员具有推送和合并权限。设置保护分支,这里的beta...

git的几种分支模式

编写代码,是软件开发交付过程的起点,发布上线,是开发工作完成的终点。代码分支模式贯穿了开发、集成和发布的整个过程,是工程师们最亲切的小伙伴。那如何根据自身的业务特点和团队规模来选择适合的分支模式呢?本文分享几种主流 Git 分支模式的流程及特点,并给出选择建议。分支的目的是隔离,但多一个分支也意味着...

代码管理-9-gitlab的使用和设置

gitlab使用1、外观设置完成后保存,返回登录页面查看关于注册,有些公司是不允许打开的,,有些人数非常多的公司就需要打开注册的功能,让人员自己注册,我们来给他特定的权限就可以,毕竟人非常多的时候还由我们来给她们注册就非常不现实了,工作量会很大2、自动注册3、组&用户&项目创建组设置组名称、描述等创...

el-table内容\n换行解决办法

问题请求到的数据带有换行符 '\n'但页面展示时不换行statusRemark: "\"1、按期完成计划且准确率100%,得100分;\n2、各项目每延误1天,扣1分;每失误1次或者员工投诉1次,扣3分,失误层面达到公司级影响较大的,该项绩效分数为0\"\n&...

别让“跑焦”毁所有!仅需这一项设置,即可显著改善镜头对焦精度

我常常会收到一些摄影爱好者的私信,也一直在努力的帮助大家解决更多摄影中常见问题。在我收到的所有问题中。有一个问题是最麻烦的,那就是“为什么我的图像看起来模糊?”。这个问题几乎每个人都遇到过,究其原因可以说是多种多样相对复杂。起初我一直认为是对焦问题所导致,也就有了我之前所写的“后按对焦”以及“对焦模...

一起学Vue:路由(vue-router)

前言学习vue-router就要先了解路由是什么?前端路由的实现原理?vue-router如何使用?等等这些问题,就是本篇要探讨的主要问题。vue-router是什么路由是什么?大概有两种说法:从路由的用途上来解释路由就是指随着浏览器地址栏的变化,展示给用户的页面也不相同。从路由的实现原理上来解释路...