在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调试,观察他们的调用栈。
首先是@Async
不生效的调用栈
@Async
生效的调用栈
通过观察我们可先发现 @Async
不生效 的调用栈中非常简单和直接一个条链路直接到断点处,在 @Async
生效 的调用栈中,我系清晰的看Spring使用AOP机制在运行
也就是说 @Async
不生效 是应为为AOP
并没有生效,至于他为什么不生效,我们可以通过AOP
的机制和织入点来判断,由于笔者能力有限,希望有人能够补充该疑问,谢谢。