在当前开发的数据分析平台中经常会涉及到较大数据集的上传与下载,由于数据处理的业务耗时较长,因此可能会导致请求超时,即使请求成功,长时间的等待也不利于用户体验。
这种情况可以考虑异步处理,Spring为我们实现了十分便利的支持,使用@EnableAsync
即可使用异步方法执行功能,使用@Async
即可开启一个线程任务。
Spring会搜索上下文中唯一的TaskExecutor
实例,或一个名为taskExecutor
的java.util.concurrent.Executor
实例;如果没有找到以上实例,则会使用 SimpleAsyncTaskExecutor
处理异步方法调用。
定义配置类
@EnableAsync(proxyTargetClass=true)
@Configuration
public class AsyncConfig {
/**
* IO密集型任务 = 一般为2*CPU核心数(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)
* CPU密集型任务 = 一般为CPU核心数+1(常出现于线程中:复杂算法)
* 混合型任务 = 视机器配置和复杂度自测而定
*/
@Bean(name = "asyncTaskExecutor")
public Executor asyncTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//1: 核心线程数目
executor.setCorePoolSize(4);
//2: 指定最大线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(10);
//3: 队列中最大的数目
executor.setQueueCapacity(200);
//4: 线程名称前缀
executor.setThreadNamePrefix("LocustTask-");
//5:当pool已经达到max size的时候,如何处理新任务
// CallerRunsPolicy: 会在execute 方法的调用线程中运行被拒绝的任务,如果执行程序已关闭,则会丢弃该任务
// AbortPolicy: 抛出java.util.concurrent.RejectedExecutionException异常
// DiscardOldestPolicy: 抛弃旧的任务
// DiscardPolicy: 抛弃当前的任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//6: 线程空闲后的最大存活时间(默认值 60),当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
//7:线程空闲时间,当线程空闲时间达到keepAliveSeconds(秒)时,线程会退出,直到线程数量等于corePoolSize,如果allowCoreThreadTimeout=true,则会直到线程数量等于0
executor.setAllowCoreThreadTimeOut(false);
executor.initialize();
return TtlExecutors.getTtlExecutor(executor);
}
}
其中@EnableAsync(proxyTargetClass=true)
用于开启对异步任务的支持,proxyTargetClass属性值决定是基于接口的还是基于类的代理被创建,如果是true,那么基于类的代理将起作用(这时需要cglib库),如果是fasle,那么标准的JDK 基于接口的代理,这个值默认为false。
测试Service
@Service
public class testAsyncService {
Logger log = LoggerFactory.getLogger(testAsyncService.class);
// 发送提醒短信 1
@Async("taskExecutor")
public void service1() throws InterruptedException {
log.info("--------start-service1------------");
Thread.sleep(5000); // 模拟耗时
log.info("--------end-service1------------");
}
// 发送提醒短信 2
@Async("taskExecutor")
public void service2() throws InterruptedException {
log.info("--------start-service2------------");
Thread.sleep(2000); // 模拟耗时
log.info("--------end-service2------------");
}
}
@Async
表示声明一个或多个异步任务,可以在类或方法上使用,在类上使用时表示整个类的方法都会使用该线程池操作。
如下方式会使@Async失效
一、异步方法使用static修饰
二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
三、异步方法不能与异步方法在同一个类中
四、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
六、在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
七、调用被@Async标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用!!!!!!!
八、使用@Async时要求是不能有返回值的不然会报错的 因为异步要求是不关心结果的