Springboot @Async 异步任务无效

在Springboot的启动类上加上

@EnableAsync

之后就可以在方法使用@Async就可以异步的调用该方法。

发现当前同一个类中调用带有@Async注解的方法,该方法并未采用异步的方式运行,而是同步。

结论:
在定义异步方法的同一个类中,调用带有@Async注解方法,无法以异步的方式运行该方法。

解决:在需要异步调用该方法时候,请在其他的类中调用。

实验

如定义一个调用带有@Async注解的方法

@Component
public class AsyncTask {
    private static final Logger logger = LoggerFactory.getLogger(AsyncTask.class);
    public void synCall() throws InterruptedException {
        logger.info("syn method Call task1 start ...");
        task1();
        logger.info("syn method Call task1 end");
    }
    @Async
    public void task1() throws InterruptedException {
        logger.info("task1 start ...");
        logger.info("Sleep {}s", 3);
        TimeUnit.SECONDS.sleep(3);
        logger.info("task1 end");
    }
}
@Autowired
private AsyncTask asyncTask;
@Test
public void testSyncTask() throws InterruptedException {
// 同步调用
    asyncTask.synCall();
}

输出

2019-03-03 13:56:59.316 [           main] : syn method Call task1 start ...
2019-03-03 13:56:59.316 [           main] : task1 start ...
2019-03-03 13:56:59.316 [           main] : Sleep 3s
2019-03-03 13:57:02.316 [           main] : task1 end
2019-03-03 13:57:02.316 [           main] : syn method Call task1 end

从上面的日志中可以发现所有日志打印都在主线程中,在打印了Sleep 3s之后,main线程睡眠了3秒之后才继续打印synCall()方法的 syn method Call task1 end。说明这段@Async注解并未生效。

在测试类中直接调用异步方法

@Test
public void testAsyncTask() throws InterruptedException {
    // 异步调用
    logger.info("Call from other class start...");
    asyncTask.task1();
    logger.info("Call from other class end");
    // 防止由于主线程提前结束,导致异步的线程被强制停止,让主线程睡眠
    TimeUnit.SECONDS.sleep(5);
}

注意:如果没有使用TimeUnit.SECONDS.sleep()使主线程睡眠,我们会因为主线运行结束之后导致异步任务的线程也结束,最后会发现没有异步任务线程的输出。

输出

2019-03-03 14:05:01.052 [           main] : Call from other class start...
2019-03-03 14:05:01.057 [           main] : Call from other class end
2019-03-03 14:05:01.062 [cTaskExecutor-1] : task1 start ...
2019-03-03 14:05:01.063 [cTaskExecutor-1] : Sleep 3s
2019-03-03 14:05:04.063 [cTaskExecutor-1] : task1 end

从上面的结果中可以发现,测试中的task1()是在main线程睡眠之后才被调用,并且从日志中可以发现运行task1()的线程为cTaskExecutor-1,说明该方法缺失以异步的方式运行。

原因分析

我们分别对两种方式进行BUG调试,观察他们的调用栈。
Springboot @Async 异步任务无效_第1张图片

首先是@Async不生效的调用栈
Springboot @Async 异步任务无效_第2张图片

@Async生效的调用栈
Springboot @Async 异步任务无效_第3张图片

通过观察我们可先发现 @Async不生效 的调用栈中非常简单和直接一个条链路直接到断点处,在 @Async生效 的调用栈中,我系清晰的看Spring使用AOP机制在运行Springboot @Async 异步任务无效_第4张图片

也就是说 @Async不生效 是应为为AOP并没有生效,至于他为什么不生效,我们可以通过AOP的机制和织入点来判断,由于笔者能力有限,希望有人能够补充该疑问,谢谢。

你可能感兴趣的:(Spring,boot)