@Async注解加上之后,是否真实现异步线程了呢?

@Async注解加上之后,是否真实现异步线程了呢?

哈喽有缘人,如果碰巧点击了这篇文章,暂时不谈原理方面,说明您对@Async注解的使用还是不够熟练哦,一步一步的,听我娓娓道来。

实现异步线程需要两步即可完成

  • 其一:在执行方法上添加注解@Async
  • 其二:在主启动类上添加注解@EnableAsync用来激活配置。

备注说明:对于线程池定义,官方强制推荐使用自定义线程池

对于线程池的定义本章暂不赘述,等后面我再梳理完知识点之后,再一起交流沟通。继续回到本章内容中,Spring5之后默认实现类为:LazyTraceThreadPoolTaskExecutor,如下图所示:在不采取自定义线程池大小时,默认采用Integer.MAX_VALUE,即最大值,一定情况下会引发OOM !!!,切记,需采用自定义线程池的大小。
ThreadPoolTaskExecutor核心属性

失效使用

  • 核心代码之Service层
/**
 * @author: Mr.Gao
 * @date: 2022年09月08日 14:28
 * @description:
 */
@Slf4j
@Component
public class AsyncClient {
    /**
     * 调用代码
     *
     * @throws Exception
     */
    public void asyncExecTest() throws Exception {
        log.info("asyncExecTest BEGIN!");
        
        // TODO 失效原因
        this.asyncTest();
        
        log.info("asyncExecTest END!");
    }
    /**
     * 异步推送处理 间隔时间为2秒 重试三次
     * @return
     */
    @Async(value = "transNotifyReplySendThread")
    @Retryable(value = Exception.class, backoff = @Backoff(value = 2000))
    public void asyncTest() throws Exception {
        log.info("接受到微信回调 开启异步推送内部工程进行处理");
        TimeUnit.SECONDS.sleep(2);
        log.info("订单异步处理成功....");
    }
}
  • 核心代码之Controller层
/**
 * @author: Mr.Gao
 * @date: 2022年09月08日 14:28
 * @description:
 */
@RestController
@RequestMapping("/async")
public class AsyncController {

    @Autowired
    private AsyncClient asyncClient;

    /**
     * 异步推送结果
     *
     * @return
     * @throws Exception
     */
    @PostMapping("/asyncExecTest")
    public BaseResMessage<Void> asyncExecTest() throws Exception {
        asyncClient.asyncExecTest();
        return BaseResMessage.build(ServiceConstants.RES_SUCCESS_CODE, "异步推送成功!");
    }
}
  • 运行结果(失效案例)
2022-09-08 17:09:26.066  INFO [tc-gateway-alipay,38bf20182499f4e0,38bf20182499f4e0,false] 98248 --- [nio-9389-exec-1] c.t.c.g.alipay.aop.AopPreHandleConfig    : tc-gateway-alipay 接口 [[ALIPAY] 异步推送结果] 接收的请求参数:null
2022-09-08 17:09:26.071  INFO [tc-gateway-alipay,38bf20182499f4e0,38bf20182499f4e0,false] 98248 --- [nio-9389-exec-1] c.t.c.gateway.alipay.client.AsyncClient  : asyncExecTest BEGIN!
2022-09-08 17:09:26.072  INFO [tc-gateway-alipay,38bf20182499f4e0,38bf20182499f4e0,false] 98248 --- [nio-9389-exec-1] c.t.c.gateway.alipay.client.AsyncClient  : 接受到微信回调 开启异步推送内部工程进行处理
2022-09-08 17:09:28.080  INFO [tc-gateway-alipay,38bf20182499f4e0,38bf20182499f4e0,false] 98248 --- [nio-9389-exec-1] c.t.c.gateway.alipay.client.AsyncClient  : 订单异步处理成功....
2022-09-08 17:09:28.080  INFO [tc-gateway-alipay,38bf20182499f4e0,38bf20182499f4e0,false] 98248 --- [nio-9389-exec-1] c.t.c.gateway.alipay.client.AsyncClient  : asyncExecTest END!
2022-09-08 17:09:28.080  INFO [tc-gateway-alipay,38bf20182499f4e0,38bf20182499f4e0,false] 98248 --- [nio-9389-exec-1] c.t.c.g.alipay.aop.AopPreHandleConfig    : tc-gateway-alipay 接口 [[ALIPAY] 异步推送结果] 返回的响应参数:code:00,msg:异步推送成功!, 耗时2014ms
  • 注意
    在这里插入图片描述

由此可以发现为同一个线程执行了,细心的同学可能发现了,TimeUnit.SECONDS.sleep(2) ;这行代码 模拟业务逻辑沉睡了 2s

  • 响应结果
    在这里插入图片描述

由此说明@Async已失效

解决方案如下

  • 1、Controller层直接调用Service的异步方法
/**
     * 异步推送结果
     *
     * @return
     * @throws Exception
     */
    @ControllerLoggerInfo("[ALIPAY] 异步推送结果")
    @PostMapping("/asyncExecTest")
    public BaseResMessage<Void> asyncExecTest() throws Exception {
        asyncClient.asyncTest();
        return BaseResMessage.build(ServiceConstants.RES_SUCCESS_CODE, "异步推送成功!");
    }
  • 2、去除this方式调用,采用SpringAOP 代理方式
public void asyncExecTest() throws Exception {
        log.info("asyncExecTest BEGIN!");
        // 采用SpringAOP 代理类进行调用
        AsyncClient notifyClient = iocClientUtil.getBean(AsyncClient.class);
        notifyClient.asyncTest();
        log.info("asyncExecTest END!");
    }
  • 执行结果
[nio-9389-exec-1] c.t.c.g.alipay.aop.AopPreHandleConfig    : tc-gateway-alipay 接口 [[ALIPAY] 异步推送结果] 接收的请求参数:null
[nio-9389-exec-1] c.t.c.g.alipay.aop.AopPreHandleConfig    : tc-gateway-alipay 接口 [[ALIPAY] 异步推送结果] 返回的响应参数:code:00,msg:异步推送成功!, 耗时6ms
[eplySendThread1] c.t.c.gateway.alipay.client.AsyncClient  : 接受到微信回调 开启异步推送内部工程进行处理
[eplySendThread1] c.t.c.gateway.alipay.client.AsyncClient  : 订单异步处理成功....

在这里插入图片描述
在这里插入图片描述

此时会发现异步线程生效了

  • 3、AopContext来获取当前代理类方式
  • 核心代码如下:
// 获取当前代理类
AsyncClient notifyClient = (AsyncClient) AopContext.currentProxy();
// 调用异步方法
notifyClient.asyncTest();
  • 继续执行,会发现报错代码
2022-09-08 17:53:20.336 ERROR [tc-gateway-alipay,774ef38467a96093,774ef38467a96093,false] 48156 --- [nio-9389-exec-1] c.t.c.g.alipay.aop.AopPreHandleConfig    : Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)
com.tc.cloud.gateway.alipay.client.AsyncClient.asyncExecTest(AsyncClient.java:37)
com.tc.cloud.gateway.alipay.client.AsyncClient$$FastClassBySpringCGLIB$$67ca3f33.invoke(<generated>)
org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
  • 在主启动类上加上配置
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
  • 或通过xml配置文件添加配置
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

总结

  • 在类方法中使用this调用异步线程方法,导致失效!!!
  • 若要想在类内部调用异步方法,需要获取代理类。因为基于Spring注解化配置,底层已然是实现了异步逻辑,如此类推,事务(@Transactional)失效的原理也是如此。
  • 基于Spring获取代理类方式如下:
    • 从SpringIOC容器中获取:
      Objetc bean = applicationContext.getBean(beanName);
      
    • 通过AopContext方式
      Object bean = AopContext.currentProxy();
      

到此,我想了下,类似于这种注解的方式,两部曲:标记(@AAA) + 启动配置(@EnableAAA);而且凡是基于Spring框架的,均需要采用代理的方式才行, 否则就会导致其失效。

题外话:
以此记录,可能文章排版有些问题,总感觉怪怪的呢,但就是说不上来,反正就是写的时候想到啥说啥,哈哈哈哈哈哈,后面我尽量调整下,把文章的深度再挖一挖。总的来说,也算是一种文字方面的提升,当然上边有说错的地方,也欢迎进行批评指正,非常感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值