SpringBoot通过线程池试实现异步任务
1 配置application.properties文件
# 异步线程配置
#核心线程数
async.executor.thread.core_pool_size=5
#配置最大线程数
async.executor.thread.max_pool_size=10
#配置队列大小
async.executor.thread.queue_capacity=10000
#配置线程池中的线程的名称前缀
async.executor.thread.name.prefix=async-service-
2 线程池配置类
先创建一个线程池的配置,让Spring Boot加载,用来定义如何创建一个ThreadPoolTaskExecutor(线程任务池)
,要使用@Configuration
和@EnableAsync
这两个注解,表示这是个配置类,并且是线程池的配置类
/**
* @author LanceQ
* @date 2021年07月15日 17:12
* 创建一个线程池配置
* @Configuration表示这是一个配置类
* @EnableAsync表示异步异步
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
private static final Logger logger= LoggerFactory.
getLogger(ExecutorConfig.class);
//@Value是配置在application.properties,自由定义
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor(){
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
//拒绝策略
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
3 异步接口和异步具体实现类
/**
* @author LanceQ
* @date 2021年07月15日 17:27
* 创建一个Service接口,是异步线程的接口
*/
public interface AsyncService {
/**
* 执行异步任务
* 可以根据需求,自己加参数定义
*/
void executeAsync();
}
/**
* @author LanceQ
* @date 2021年07月15日 17:28
* 具体实现类
* 将Service层的服务异步化,在executeAsync()方法上增加注解@Async("asyncServiceExecutor"),
* asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,
* 表明executeAsync方法进入的线程池是asyncServiceExecutor方法创建的
*/
@Service
public class AsyncServiceImpl implements AsyncService{
private static final Logger logger= LoggerFactory.
getLogger(ExecutorConfig.class);
@Override
@Async("asyncServiceExecutor")
public void executeAsync() {
logger.info("start executeAsync");
System.out.println("异步线程要做的事情");
System.out.println("执行批量插入等操作的耗时的事件");
logger.info("end executeAsync");
System.out.println("---------------------");
}
}
4 Controller调用接口
/**
* @author LanceQ
* @date 2021年07月15日 17:33
*/
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async")
public void async(){
asyncService.executeAsync();
}
}
5 运行结果
运行SpringBoot项目后不停,输入http://localhost:8080/async,不停刷新页面,将在控制台显示下面的结果
运行结果:
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:57:51.885 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:57:55.024 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:57:55.026 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:01.913 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:01.915 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:02.113 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:02.114 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:02.271 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:02.272 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:02.449 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:02.449 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:02.616 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:02.617 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:02.776 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:02.776 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:02.943 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:02.944 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:03.103 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:03.103 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:03.281 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:03.281 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:03.439 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:03.439 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:03.617 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:03.617 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:03.791 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:03.791 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:03.975 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:03.975 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:04.175 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:04.175 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:04.330 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:04.331 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:04.502 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:04.502 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:04.686 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:04.686 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:04.871 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:04.871 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:05.049 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:05.049 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:05.233 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:05.233 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:05.575 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:05.575 INFO 4412 --- [async-service-3] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:05.774 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:05.775 INFO 4412 --- [async-service-4] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:05.959 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:05.959 INFO 4412 --- [async-service-5] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:06.335 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:06.335 INFO 4412 --- [async-service-1] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
2021-07-15 19:58:06.512 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : start executeAsync
异步线程要做的事情
执行批量插入等操作的耗时的事件
2021-07-15 19:58:06.513 INFO 4412 --- [async-service-2] com.example.thpool.ExecutorConfig : end executeAsync
---------------------
通过以上日志可以发现,[async-service-]是有多个线程的,显然已经在我们配置的线程池中执行了,并且每次请求中,controller的起始和结束日志都是连续打印的,表明每次请求都快速响应了,而耗时的操作都留给线程池中的线程去异步执行;
6 自定义线程池显示信息
/**
* @author LanceQ
* @date 2021年07月15日 18:44
*/
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private final static Logger logger= LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
/**
* @param prefix
* showThreadPoolInfo方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,
* 然后Override了父类的execute、submit等方法,在里面调用showThreadPoolInfo方法,
* 这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;
*/
private void showThreadPoolInfo(String prefix){
ThreadPoolExecutor threadPoolExecutor=getThreadPoolExecutor();
if(threadPoolExecutor==null){
return;
}
logger.info("{},{},taskCount [{}] , completedTaskCount [{}] , activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size()
);
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}