线程池解析请看Java 线程池详解
一、 配置线程池
主要使用到 @Configuration @EnableAsync这两个注解,从字面上可以看出,前者是自定义配置类,后者是使能线程池。 springboot中主要使用配置类来配置线程池 @Async注解可以使用配置好的线程池 其他的配置例如yml或者properties文件,按照springboot的正常配置就行。
@EnableAsync:表示开启对异步任务的支持,可以放在springboot的启动类上,也可以放在自定义线程池的配置类上
@Async:对于异步方法调用,从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。如果配置了多个线程池,可以用@Async("name"),那么表示线程池的@Bean的name,来指定用哪个线程池处理
下面是我的一个配置类示例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author leo
* @date 2023/2/7 11:33
* TODO:线程池的配置
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
@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;
/**
* TODO: 此方法名称为asyncMainExecutor,即在spring中注入了一个名字为asyncMainExecutor的bean
* 方法名只要在项目中唯一性,可以适当任意取(最好遵循一定的规则)
* 使用方法:在需要加入线程池的方法上增加注解@Async("asyncMainExecutor")就可以加入此线程池异步执行
*
* @return
* @throws
* @author leo
* @time 2023/2/7 11:33
* @params
*/
@Bean
public Executor asyncMainExecutor() {
logger.info("start asyncMainExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
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;
}
}
配置文件配置
# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size = 5
# 配置最大线程数
async.executor.thread.max_pool_size = 5
# 配置队列大小
async.executor.thread.queue_capacity = 99999
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix = async-main-
二、创建一个Service接口,是异步线程的接口
public interface AsyncMainService {
/** * 执行异步任务 * 可以根据需求,自己加参数拟定,我这里就做个测试演示 */
void executeAsync();
}
实现
@Service
public class AsyncMainServiceImpl implements AsyncMainService {
private static final Logger logger = LoggerFactory.getLogger(AsyncMainServiceImpl.class);
@Override
@Async("asyncMainExecutor")
public void executeAsync() {
logger.info("start executeAsync");
System.out.println("异步线程要做的事情");
System.out.println("可以在这里执行批量插入等耗时的事情");
logger.info("end executeAsync");
}
}
三、Controller实现
@Autowired
private AsyncMainService asyncMainService;
@GetMapping("/async")
public void async(){
asyncMainService.executeAsync();
}
四、重写ThreadPoolTaskExecutor
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
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("do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo(" do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo(" do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
如上所示,showThreadPoolInfo方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后Override了父类的execute、submit等方法,在里面调用showThreadPoolInfo方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中
修改ExecutorConfig.java的asyncServiceExecutor方法,将ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改为ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor()
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author leo
* @date 2023/2/7 11:33
* TODO:线程池的配置
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
@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;
/**
* TODO: 此方法名称为asyncMainExecutor,即在spring中注入了一个名字为asyncMainExecutor的bean
* 方法名只要在项目中唯一性,可以适当任意取(最好遵循一定的规则)
* 使用方法:在需要加入线程池的方法上增加注解@Async("asyncMainExecutor")就可以加入此线程池异步执行
*
* @return
* @throws
* @author leo
* @time 2023/2/7 11:33
* @params
*/
@Bean
public Executor asyncMainExecutor() {
logger.info("start asyncMainExecutor");
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;
}
}
启动工程,就能打印日志数据了
五、知识总结
corePoolSize,maximumPoolSize,workQueue之间关系
- 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
- 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
- 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
- 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
- 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
- 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
大家好,我是Doker品牌的Sinbad,欢迎点赞和评论,大家的鼓励是我们持续更新的动力!或者加微信进入技术群聊!