不看绝对后悔的@Async深度解析

一.前言

在整理老的业务逻辑代码时候发现好多接口实现上面都标记了 @Async注解。我本身对这个注解使用的比较少,异步逻辑我都习惯自定义ThreadPoolExecutor工具类。正好借着这次梳理代码结构,来看看 @Async这个注解到底在玩什么?

本文将会给大家从 @Async注解使用层面入手逐步解读源码,分析各种踩坑实践,并且扩展sleuth链路追踪线程变量如何花式应用。

二.尝鲜使用

Spring中,被 @Async注解标注的方法,称之为异步方法。这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作,是spring默认提供的异步调用方式。

2.1.使用方式

使用 @Async进行异步变成的方式特别简单。

  1. 启动类或者能被启动类扫描到的配置类上标注@EnableAsync
  2. 被spring管理的bean的方法上标注@Async()
  3. 调用方法被调用方法不在同一个bean中。

仔细品味一下上面三个限制条件,任意一个不满足,均会导致 @Async无法生效

2.2.最简单的demo演示

启动类定义

@SpringBootApplication
@EnableAsync
public class DemoApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
​
}
复制代码

controller层方法定义

@RestController
@RequestMapping
public class TestController {
​
    @Autowired
    TestService testService;
​
    @GetMapping()
    public void test(){
        for (int i = 0; i <5 ; i++) {
            testService.testAsync();
        }
    }
}
复制代码

service方法定义

@Service
@Slf4j
public class TestServiceImpl implements TestService {
​
    @Override
    @Async
    public void testAsync(){
        log.info("嘻嘻");
    }
}
复制代码

日志输出

- 2021-09-15 19:39:54.300,[http-nio-8088-exec-5], com.examp.controller.TestController - 嘿嘿
- 2021-09-15 19:36:54.302,[task-5], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-4], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-1], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-2], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-3], com.examp.service.impl.TestServiceImpl - 嘻嘻
复制代码

从日志打印可以发现,controller方法打印service层方法打印日志使用的是不同的线程

使用是真滴简单!

2.3.踩坑提问

前面两点可以看到我们使用@Async进行异步变成是真的简单,但是里面也埋伏了各种各样的坑点。

先抛出问题,大家可以先思考:

  1. 为什么阿里不推荐直接使用@Async
  2. @Async标注的方法是否事务一致
  3. 同一个类里面A->B,B方法上标注了@Async,为了调用成功,在类中注入当前类方式能否异步调用成功
  4. @Async标注的方法能否读取到ThreadLocal的变量
  5. @Async标注的方法能否获取返回值
  6. slueth链路追踪的traceId能否追踪到线程池内、

三.源码分析

废话不多说,看看源码。

3.1.@Async

/**
* 该注解可以标记一个异步执行的方法,也可以用来标注类,表示类中的所有方法都是异步执行的。
* 入参随意,但返回值只能是void或者Future.(ListenableFuture接口/CompletableFuture类)
* Future是代理返回的切实的异步返回,用以追踪异步方法的返回值。当然也可以使用AsyncResult类(实现
* ListenableFuture接口)(Spring或者EJB都有)或者CompletableFuture类
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
​
   //用以限定执行方法的执行器名称(自定义):Executor或者TaskExecutor
   //加在类上表示整个类都使用,加在方法上会覆盖类上的设置
   String value() default "";
​
}
复制代码

3.2.@EnableAsync

/**
* 开启spring异步执行器,类似xml中的task标签配置,需要联合@Configuration注解一起使用,对应文章开头,注解需* 要标注在启动类或者能被启动类扫描到的配置类上。
*
* 默认情况下spring会先搜索TaskExecutor类型的bean或者名字为taskExecutor的Executor类型的bean,都不存在使* 用SimpleAsyncTaskExecutor执行器
*
* 可实现AsyncConfigurer接口复写getAsyncExecutor获取异步执行器,getAsyncUncaughtExceptionHandler获* 取异步未捕获异常处理器
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
​
   // 该属性用来支持用户自定义异步注解,默认扫描spring的@Async和EJB3.1的@code @javax.ejb.Asynchronous
   Class<? extends Annotation> annotation() default Annotation.class;
​
   //标明是否需要创建CGLIB子类代理,AdviceMode=PROXY时才适用。注意设置为true时,其它spring管理的bean也会升级到CGLIB子类代理
   boolean proxyTargetClass() default false;
​
   //标明异步通知将会如何实现,默认PROXY,如需支持同一个类中非异步方法调用另一个异步方法,需要设置为ASPECTJ
   AdviceMode mode() default AdviceMode.PROXY;
​
   //标明异步注解bean处理器应该遵循的执行顺序,默认最低的优先级(Integer.MAX_VALUE,值越小优先级越高)
   int order() default Ordered.LOWEST_PRECEDENCE;
​
}
复制代码

3.3.Async

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
@Async方法是Spring框架中的一种特殊注解,用于实现异步方法调用。通过在方法上添加@Async注解,可以告诉Spring容器该方法应该在单独的线程中执行,而不会阻塞当前线程的执行。\[1\] 要使用@Async方法调用,需要按照以下步骤进行操作: 1. 在启动类上添加@EnableAsync注解,以启用异步功能。\[2\] 2. 在需要异步执行的方法上添加@Async注解。这样,当调用该方法时,Spring会自动将其放入线程池中执行,并返回一个Future对象,用于获取异步方法的执行结果。\[3\] 例如,假设我们有一个名为doSomething的方法需要异步执行,我们可以在方法上添加@Async注解,如下所示: ```java @Async public Future<String> doSomething() { // 异步执行的逻辑 return new AsyncResult<>("异步方法执行结果"); } ``` 在调用doSomething方法时,它将在单独的线程中执行,并返回一个Future对象。我们可以使用该对象来获取异步方法的执行结果,如下所示: ```java Future<String> futureResult = doSomething(); String result = futureResult.get(); // 获取异步方法的执行结果 ``` 通过使用@Async方法调用,我们可以实现并发执行任务,提高系统的响应性能和吞吐量。同时,它也可以帮助我们处理那些不需要立即返回结果的耗时操作,提高系统的并发处理能力。 #### 引用[.reference_title] - *1* *3* [在Spring中使用Future对象调用Async方法调用](https://blog.csdn.net/dnc8371/article/details/106705454)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring中使用@Async异步调用方法](https://blog.csdn.net/qq_34178998/article/details/95939425)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值