1.使用@EnableAsync注解开启异步,这个注解一般放在启动类或者线程池配置类上,这里我放在线程池配置类上。
2.编写线程池配置类
package com.demo.config;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* 配置线程池
*/
@EnableAsync //注解启用Spring 异步方法执行功能
@Configuration
public class AsyncTaskConfig {
/**
* 核心线程数
*/
@Value("${threadPool.corePoolSize}")
private int CORE_POOL_SIZE;
/**
* 最大线程数
*/
@Value("${threadPool.maxPoolSize}")
private int MAX_POOL_SIZE;
/**
* 队列长度
*/
@Value("${threadPool.queueCapacity}")
private int QUEUE_CAPACITY;
/**
* 线程池维护线程所允许的空闲时间
*/
@Value("${threadPool.keepAliveSeconds}")
private int KEEP_ALIVE_SECONDS;
@Bean("AsyncTaskThreadPool")
public Executor threadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:线程池创建时候初始化的线程数
executor.setCorePoolSize(CORE_POOL_SIZE);
// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(MAX_POOL_SIZE);
// 缓冲队列:用来缓冲执行任务的队列
executor.setQueueCapacity(QUEUE_CAPACITY);
// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("AsyncTaskThreadPool-");
// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
//AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常
//DiscardPolicy:丢弃任务,但是不抛出异常。可能导致无法发现系统的异常状态
//DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
//CallerRunsPolicy:不丢弃任务 由调用线程处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
executor.setAllowCoreThreadTimeOut(true);
executor.initialize();
return executor;
}
}
线程池的相关参数,我放到了配置文件中,后续可以根据实际情况调整参数
3.使用@Async注解标记需要异步执行的方法,并在注解内指定配置的线程池执行器名称就可以将异步线程交给线程池来管理
@Async注解实现异步任务是通过代理类技术实现的,在使用中需要注意@Async失效的一些情况,避免异步任务失效
(1)注解@Async的方法所在类需要交由spring管理,检查是否扫描到。
(2)注解@Async只能应用在public方法上
(3)调用方与被调方不能在同一个类(同一个类方法之间调用不会通过代理调用因此异步任务会失效)
一般我们会把异步执行任务的方法抽离成一个类,使用spring注入的方式来使用
例如:
package com.demo.task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.io.File;
@Component
public class ExecuteTask {
private static final Logger logger = LoggerFactory.getLogger(ExecuteTask.class);
@Async("AsyncTaskThreadPool")
public void testExecuteTask(Integer sort) {
logger.info("异步线程开始执行===================sort={}", sort);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
调用:
调用结果:
如上例子,我们使用spring boot注解快捷的开启了异步任务,并且配置线程池进行管理