什么是异步调用?
异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行。异步调用指,在程序在执行时,无需等待执行的返回值即可继续执行后面的代码。在我们的应用服务中,有很多业务逻辑的执行操作不需要同步返回(如发送邮件、冗余数据表等),只需要异步执行即可。
本文将介绍 Spring 应用中,如何实现异步调用。在异步调用的过程中,会出现线程上下文信息的丢失,我们该如何解决线程上下文信息的传递。
Spring 应用中实现异步
Spring 为任务调度与异步方法执行提供了注解支持。通过在方法或类上设置 @Async 注解,可使得方法被异步调用。调用者会在调用时立即返回,而被调用方法的实际执行是交给 Spring 的 TaskExecutor 来完成的。所以被注解的方法被调用的时候,会在新的线程中执行,而调用它的方法会在原线程中执行,这样可以避免阻塞,以及保证任务的实时性。
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 复制代码
引入 Spring 相关的依赖即可。
入口类
@SpringBootApplication @EnableAsync public class AsyncApplication { public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); } 复制代码
入口类增加了 @EnableAsync 注解,主要是为了扫描范围包下的所有 @Async 注解。
对外的接口
这里写了一个简单的接口:
@RestController @Slf4j public class TaskController { @Autowired private TaskService taskService; @GetMapping("/task") public String taskExecute() { try { taskService.doTaskOne(); taskService.doTaskTwo(); taskService.doTaskThree(); } catch (Exception e) { log.error("error executing task for {}",e.getMessage()); } return "ok"; } } 复制代码
调用 TaskService 执行三个异步方法。
Service 方法
@Component @Slf4j //@Async public class TaskService { @Async public void doTaskOne() throws Exception { log.info("开始做任务一"); long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); log.info("完成任务一,耗时:" + (end - start) + "毫秒"); } @Async public void doTaskTwo() throws Exception { log.info("开始做任务二"); long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); log.info("完成任务二,耗时:" + (end - start) + "毫秒"); } @Async public void doTaskThree() throws Exception { log.info("开始做任务三"); long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); log.info("完成任务三,耗时:" + (end - start) + "毫秒"); } } 复制代码
@Async 可以用于类上,标识该类的所有方法都是异步方法,也可以单独用于某些方法。每个方法都会 sleep 1000 ms。
结果展示
运行结果如下:
可以看到 TaskService 中的三个方法是异步执行的,接口的结果快速返回,日志信息异步输出。异步调用,通过开启新的线程调用的方法,