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

本文详细解析了Spring中的@Async注解,从使用方式、源码分析到常见问题解答,探讨了异步方法的实现原理,包括线程池的配置、事务一致性、类内部调用、ThreadLocal变量以及Sleuth链路追踪。同时提醒在使用@Async时需注意线程池配置和避免循环依赖。
摘要由CSDN通过智能技术生成

一.前言

在整理老的业务逻辑代码时候发现好多接口实现上面都标记了 @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.LOW
  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值