问题
在用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的线程本身来执行。
网友评论