问题
在用springboot框架做定时任务的时候,大部分情况都是直接通过@Scheduled注解来指定定时任务的。但是当你有多个定时任务时,@Scheduled并不一定会按时执行。
因为使用@Scheduled的定时任务虽然是异步执行的,但是,默认不同的定时任务之间并不是并行的。
查看org.springframework.scheduling.config.ScheduledTaskRegistrar源码即可发现
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
if (this.triggerTasks != null) {
for (TriggerTask task : this.triggerTasks) {
addScheduledTask(scheduleTriggerTask(task));
}
}
if (this.cronTasks != null) {
for (CronTask task : this.cronTasks) {
addScheduledTask(scheduleCronTask(task));
}
}
if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) {
addScheduledTask(scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) {
addScheduledTask(scheduleFixedDelayTask(task));
}
}
}
当未手动指定taskScheduler时,会通过Executors.newSingleThreadScheduledExecutor()创建默认的单线程线程池,且该线程池的拒绝策略为AbortPolicy,这种策略在线程池无可用线程时丢弃任务,并抛出异常RejectedExecutionException。
解决方法
添加配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executors;
/**
* @author maokeluo
* @description 多隆镇楼,bug退散🙏🙏🙏
* 定时任务线程池配置
* @date 2019/12/31
*/
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
//显式为Scheduler指定线程池
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
}
}
补充
@Scheduled注解参数:
cron:cron表达式,指定任务在特定时间执行;
fixedDelay:表示上一次任务执行完成后多久再次执行,参数类型为long,单位ms;
fixedDelayString:与fixedDelay含义一样,只是参数类型变为String;
fixedRate:表示按一定的频率执行任务,参数类型为long,单位ms;
fixedRateString: 与fixedRate的含义一样,只是将参数类型变为String;
initialDelay:表示延迟多久再第一次执行任务,参数类型为long,单位ms;
initialDelayString:与initialDelay的含义一样,只是将参数类型变为String;
zone:时区,默认为当前时区。
线程池拒绝策略
接口java.util.concurrent.RejectedExecutionHandler提供了拒绝任务处理的自定义方法。在java.util.concurrent.ThreadPoolExecutor中已经包含四种拒绝策略。
AbortPolicy拒绝策略:抛出运行时异常RejectedExecutionException,这种策略丢弃任务,并抛出异常(jdk默认策略)。
DiscardPolicy拒绝策略:不能执行的任务将被丢弃,这种策略什么都没做。
DiscardOldestPolicy拒绝策略:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序。
CallerRunsPolicy拒绝策略:线程调用运行该任务的execute本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。这个策略不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。
yml中直接配置
spring:
task:
scheduling:
pool:
size: 10
评论区