侧边栏壁纸
博主头像
丛庆

没事儿写代码,有事写代码。email:1024@cong.zone

  • 累计撰写 116 篇文章
  • 累计创建 97 个标签
  • 累计收到 4 条评论

【Java】【SpringBoot】SpringBoot @Scheduled多线程执行

丛庆
2021-12-16 / 0 评论 / 0 点赞 / 639 阅读 / 2,350 字 / 正在检测是否收录...
温馨提示:
部分资料和图片来源于网络,如有危害到您的利益请与我联系删除,1024@cong.zone。

问题
在用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
0

评论区