-
使用@Async注解来实现异步操作(不需要等待的,都是同时执行的,不用等待第一个任务结束以后在调用后面的任务),这里假设一个场景,比如导出excel表格,数据量非常的大,比如有有一千万数据,导出的时候有时候会出现连接超时,数据还没有导出完。这里我们就用异步去导出这个excel,我们还是正常的返回,但是导出放在后台让他自己运行就行了。
-
使用@Async注解其实就是简化了多线程的操作,多线程也是可以达到这种效果的,多个人任务同时执行,就不会把时间浪费在等待的时间上面去了
-
这里我贴上别人的解释
在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率。今天我们来探讨下 spring 是如何完成这个功能的。spring 在扫描bean的时候会扫描方法上是否包含@async的注解,如果包含的,spring会为这个bean动态的生成一个子类,我们称之为代理类(?),代理类是继承我们所写的bean的,然后把代理类注入进来,那此时,在执行此方法的时候,会到代理类中,代理类判断了此方法需要异步执行,就不会调用父类(我们原本写的bean)的对应方法。spring自己维护了一个队列,他会把需要执行的方法,放入队列中,等待线程池去读取这个队列,完成方法的执行,从而完成了异步的功能。我们可以关注到再配置task的时候,是有参数让我们配置线程池的数量的。因为这种实现方法,所以在同一个类中的方法调用,添加@async注解是失效的!,原因是当你在同一个类中的时候,方法调用是在类体内执行的,spring无法截获这个方法调用。
那在深入一步,spring为我们提供了AOP,面向切面的功能。他的原理和异步注解的原理是类似的,spring在启动容器的时候,会扫描切面所定义的类。在这些类被注入的时候,所注入的也是代理类,当你调用这些方法的时候,本质上是调用的代理类。通过代理类再去执行父类相对应的方法,那spring只需要在调用之前和之后执行某段代码就完成了AOP的实现了!
-
下面时具体的实现代码
-
首先我们先定义一个线程池,异步操作也是需要线程池的
package com.zhou.demo.threadpool;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
/**
* @author Mr.zhou
* 定义线程池
*/
@Component
@EnableAsync
public class ThreadExecutorConfig {
private static final int corePoolSize = 10;
private static final int maxPoolSize = 200;
private static final int queueCapacity = 10;
@Bean
public static Executor smartExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize); // 线程池的核心大小
executor.setMaxPoolSize(maxPoolSize); // 线程池的最大数量
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("smartExecutor"); //线程池的名称 可以自己定义
executor.initialize();
return executor;
}
}
- 业务需求,需要执行的任务,记得加上@Component
package com.zhou.demo.threadpool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
* @author Mr.zhou
*/
// Component 将这个类加载到spring容器当中 Slf4j是springboot自带的日志框架
@Component
@Slf4j
public class AsyncTask {
// 生成一个随机数
private static Random random = new Random();
// 开启异步操作
@Async
public void task() throws InterruptedException {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
// 让这个线程睡眠,让测试效果更加的明显
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}
// 开启异步操作
@Async
public void taskAsync1() throws Exception {
// 让这个线程睡眠,让测试效果更加的明显
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
log.info("##########################" + i);
}
}
}
- 使用Controller进行调用实现层
package com.zhou.demo.threadpool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.Executor;
/**
* @author Mr.zhou
*/
@RestController
@RequestMapping("api/task")
@Slf4j
public class TaskController{
/**
* 这里使用构造函数注入,spring官方文档推荐的
* 也可以用注解的形式注入
*/
private AsyncTask asyncTask;
public TaskController(AsyncTask asyncTask) {
this.asyncTask = asyncTask;
}
@RequestMapping(value = "/async", method = RequestMethod.GET)
public String task1() throws Exception {
log.info("test async................");
asyncTask.task();
asyncTask.taskAsync1();
return "执行成功";
}
}
- 当我们请求接口的时候:localhost:8080/api/task/async,直接返回了,但是后台还是在执行的