Spring Boot 进阶——如何使用@Async注解提升API并发
@Async注解用法
1. 在方法上使用@Async注解,申明该方法是一个异步任务;
2. 在类上面使用@Async注解,申明该类中的所有方法都是异步任务;
3. 使用此注解的方法的类对象,必须是spring管理下的bean对象;
4. Spring Boot启动类中增加@EnableAsync
a.@Async注解在使用时,如果不指定线程池的名称,则使用Spring默认的线程池,Spring默认的线程池为SimpleAsyncTaskExecutor。
b.方法上一旦标记了@Async注解,当其它线程调用这个方法时,就会开启一个新的子线程去异步处理该业务逻辑。
一、异步调用类——Task.java
@Component
public class Task {
public static Random random = new Random();
@Async
public Future<String> doTackOne() throws InterruptedException {
TimeUnit.SECONDS.sleep(random.nextInt(10));
return new AsyncResult<>("任务一完成");
}
@Async
public Future<String> doTackTwo() throws InterruptedException {
TimeUnit.SECONDS.sleep(random.nextInt(10));
return new AsyncResult<>("任务二完成");
}
@Async
public Future<String> doTackThree() throws InterruptedException {
TimeUnit.SECONDS.sleep(random.nextInt(10));
return new AsyncResult<>("任务三完成");
}
}
任务一执行完成的时间是不固定的,可能1秒执行完成也可能10秒执行完成。如果其中一个任务执行时间较长的话可能会影响到其他任务的执行。其实从逻辑上看,三个任务之间并没有明确的因果关系,第二个任务的执行并不需要第一个任务执行的返回结果做为依赖。这个时候。我们就可以考虑采用异步调用的方式。
会看到我们在每个执行任务的方法上都加上了@Async 注解,通过这个注解就可以让我们的方法执行变成异步执行。
当然光有@Async注解是不够的,我们还要在主启动类上标注@EnableAsync,注解允许应用程序通过@Async 注解来进行异步执行,当然我们也可以专门写一个配置类在配置类中添加@EnableAsync注解。
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
接下来,我们就需要获取到对应的返回值并且对返回值进行判断是否任务执行完成。这将如何实现呢?
package com.nijia.user.controller;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.nijia.user.service.impl.Task;
@RestController
@RequestMapping("/open")
public class TaskController {
@Autowired
private Task task;
@GetMapping("/hello")
public String hello() throws InterruptedException {
Future<String> doTackOne = task.doTackOne();
Future<String> doTackTwo = task.doTackTwo();
Future<String> doTackThree = task.doTackThree();
// 设置自旋等待
while (true) {
boolean isDone = doTackOne.isDone() && doTackTwo.isDone() && doTackThree.isDone();
if (isDone) {
break;
}
TimeUnit.SECONDS.sleep(1);
}
System.out.println("所有任务完成");
return "Task";
}
}
这个时候,我们通过理论计算可以知道,要想三个任务同时完成,那么它所用的时间应该是任务执行时间最长的哪个任务决定,也就是说如果任务三执行的时间是2秒,任务二执行的时间是3秒,任务一执行的时间是4秒,那么最终完成所有任务所用的时间应该是4秒左右。而如果采用同步的方式我们可以计算的到完成所有任务的总用时是三者之和,也就是9秒。
从这里可以知道,采用异步的方式为整个的过程调用节省了5秒的等待时间。可见异步调用在一些并发项目中确实可以节省不少时间提升接口调用效率。