springboot 与线程池和定时任务
前言 :
最近遇到的定时任务处理文件的需求比较多,所以简单记录下。
版本 :
- jdk 1.8
- springboot 2.3.4
定时任务 :
springboot 中的定时任务目前有三种实现方式:
- 基于注解 @Scheduled
- 基于接口 SchedulingCongfigurer
- 基于注解实现多线程定时任务
基于注解 :
// 基于注解是最简单的实现方式,可以通过 cron 表达式来设置其执行时间,也可通过
// fixedRate 来设置其执行时间,同时还提供了通过 fixedDelay 来设置其延迟时间。
// 缺点是其默认是单线程的,在定时任务过多的情况下会影响程序性能,且若需修改其
// 执行时间则需重启服务
@Configuration
@EnableScheduling // 开启定时任务,此注解可加到启动类上
public class TestSchedule {
// 5s 执行一次,表达式位分别代表 秒 分 时 日 月 周,详见 www.baidu.com
@Scheduled(cron = "0/5 * * * * ?")
public void schedule() {
// do something
}
}
基于接口 SchedulingConfigurer :
// 基于接口实现最明显的特点在于定时任务在每次执行时都会重新设置下一次的执行时间,
//而在此处我没可以将其设计成从数据库动态加载,这样若需修改定时任务执行时间只需直
// 接修改数据库即可。缺点是依旧是单线程的,局限于性能,需注意,若数据库时间格式
// 错误则定时任务停止执行,需重启服务才可
@Configuration
@EnableScheduling
public class TestSchedulingConfigurer implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() { // 添加定时任务
@Override
public void run() {
// do something
}
}, new Trigger() { // 设置执行时间
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// 此处实参可从数据库加载
CronTrigger trigger = new CronTrigger("0/5 * * * * ?");
return trigger.nextExecutionTime(triggerContext);
}
});
// 若有多个定时任务则可继续 taskRegistrar.addTriggerTask()
}
}
基于注解实现多线程定时任务 :
// 基于注解的多线程定时任务是在注解 @Schedule 的作用下开启多线程,这样可以使程序
// 以多线程的方式执行定时任务,从而提升程序性能。此处的注解 @EnableAsync 默认用
// 八个线程来执行定时任务(不晓得是跟系统有关还是默认的)
@Configuration
@EnableScheduling // 开启定时任务,此注解可加到启动类上
@EnableAsync // 开启多线程,此诸结可加到启动类上
public class TestSchedules {
// 5s 执行一次,表达式位分别代表 秒 分 时 日 月 周,详见 www.baidu.com
@Async
@Scheduled(cron = "0/5 * * * * ?")
public void scheduleOne() {
// do something
}
@Async
@Scheduled(cron = "0/5 * * * * ?")
public void scheduleTwo() {
// do something
}
}
线程池 :
jdk 为我们提供了 Executor 接口来创建线程池,但在实现上不是很方便。spring 为我们提供了 ThreadPoolTaskExecutor 和 ThreadPoolTaskScheduler 来创建线程池,分别用来执行任务和调度任务,其中 ThreadPoolTaskScheduler 结合 @Scheduled 和 @EnableAsync 注解可以更优雅方便的实现多线程定时任务,再结合 ThreadPoolTaskExecutor 可以更灵活的实现定时任务和文件处理的需求。
@EnableAsync
@Configuration
public class ThreadPoolConfig {
// 调度线程池
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(2); // 定时任务线程数
taskScheduler.setThreadNamePrefix("schedule-task-"); // 线程名前缀
taskScheduler.initialize();
return taskScheduler;
}
// 执行线程池
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(2); // 核心线程数
taskExecutor.setMaxPoolSize(5); // 最大线程数
taskExecutor.setQueueCapacity(99999); // 队列大小 默认 int 最大值 2^31 - 1
taskExecutor.setThreadNamePrefix("momo-task-"); // 线程池中线程名称前缀
// 当线程池中线程数量达到 max pool size 时,使用线程池调用者所在的线程来执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.initialize(); // 初始化
return taskExecutor;
}
}
// 使用,注入 ThreadPoolTaskExecutor 然后调用 executor.execute(new Runable) 方法即可
// executor.execute(new Runable)
1、如果此时线程池线程的数量小于核心线程数,即使有空闲线程存在也会创建新的线
程来处理任务
2、如果此时线程池线程的数量等于核心线程数,且任务队列未满,则新到任务将放入
任务队列
3、如果此时线程池线程的数量大于核心线程数且小于最大线程数,且任务队列已满,则
创建新的线程来处理新到的任务
4、如果此时线程池线程的数量等于最大线程数,且任务队列已满,则根据 handler 所
设置的策略来处理新到的任务
5、如果此时线程池线程的数量大于核心线程数,且某个线程的空闲时间超过线程存活时
间(kep-alive-time),则该线程将被终止
@XGLLHZ-《任我行》.mp3-肥橙
版权声明:本文为XGLLHZ原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。