基于springboot,自定义线程池提高定时任务执行效率
- 在主类上开启@EnableScheduling和@EnableAsync以执行定时任务
/**
* mallAdminApplication
*
* @author lyc
**/
@EnableDubboConfiguration
@SpringBootApplication(exclude = {SpringBootConfiguration.class, DruidDataSourceAutoConfigure.class, DataSourceAutoConfiguration.class})
@EnableSwaggerAutoConfig
@EnableScheduling
public class MallAdminApplication {
public static void main(String[] args) {
SpringApplication.run(MallAdminApplication.class, args);
}
}
- 配置线程池
/**
* 线程池配置
*
* @author lyc
**/
@Configuration
@Slf4j
public class ThreadPoolConfig {
@Bean
public Executor asyncServiceExecutor() {
log.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(5);
//配置最大线程数
executor.setMaxPoolSize(5);
//配置队列大小
executor.setQueueCapacity(99999);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("async-service-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
- 以下模拟每隔一秒钟分别向两张表中插入数据,可用于向数据库中快速插入一些随机数据用于测试的需要
/**
* 定时任务,用于添加测试数据
*
* @author lyc
**/
@Component
@Slf4j
public class ScheduledTaskConfig {
@Reference
private IPmsProductService pmsProductService;
@Reference
private IOmsOrderService omsOrderService;
@Reference
private IUmsMemberService umsMemberService;
private Snowflake snowflake = IdUtil.getSnowflake(1L, 1L);
/**
*使用自定义的线程池执行任务
**/
@Autowired
@Qualifier("asyncServiceExecutor")
private Executor executor;
@Scheduled(cron = "*/1 * * * * ?")
@Async("asyncServiceExecutor")
public void scheduledInsertPmsProduct() {
for (int i = 0; i < 100; i++) {
CompletableFuture.runAsync(() -> {
String randomUUID = IdUtil.randomUUID();
String name = "商品名称" + randomUUID;
log.info(">>>>>>>>>>>>>开始插入商品数据: {}", name);
pmsProductService.save(new PmsProduct().setName(name)
.setDescription("商品描述" + randomUUID)
.setProductSn("no" + randomUUID));
}, executor);
}
}
@Scheduled(cron = "*/1 * * * * ?")
@Async("asyncServiceExecutor")
public void scheduledInsertUmsMember() {
for (int i = 0; i < 100; i++) {
CompletableFuture.runAsync(() -> {
String snowflakeStr = snowflake.nextIdStr();
String name = "user" + snowflakeStr;
String nickname = "昵称" + snowflakeStr;
Random random = new Random();
log.info(">>>>>>>>>>>>>开始插入用户数据: {}", name);
umsMemberService.save(new UmsMember()
.setNickname(nickname)
.setUsername(name)
.setPassword(snowflakeStr)
.setGender(random.nextInt(3)));
}, executor);
}
}
}
线程池的任务执行机制
任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。
首先,所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:
首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
参考:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html