美文网首页
spring中定时任务

spring中定时任务

作者: huoyl0410 | 来源:发表于2018-03-29 11:38 被阅读0次

    参考springBoot中@Scheduled执行原理解析

    spring在初始化bean后,通过“postProcessAfterInitialization”拦截到所有的用到“@Scheduled”注解的方法,并解析相应的的注解参数,放入“定时任务列表”等待后续处理;之后再“定时任务列表”中统一执行相应的定时任务(任务为顺序执行,先执行cron,之后再执行fixedRate)。

    第一步:依次加载所有的实现Scheduled注解的类方法。

    //说明:ScheduledAnnotationBeanPostProcessor继承BeanPostProcessor。

    @Override

    public Object postProcessAfterInitialization(final Object bean, String beanName) {

    //省略多个判断条件代码

          for (Map.Entry> entry : annotatedMethods.entrySet()) {

                  Method method = entry.getKey();

                   for (Scheduled scheduled : entry.getValue()) {

                             processScheduled(scheduled, method, bean);

                    }

              }

           }

    return bean;

    }

    第二步:将对应类型的定时器放入相应的“定时任务列表”中。

    //说明:ScheduledAnnotationBeanPostProcessor继承BeanPostProcessor。

    //获取scheduled类参数,之后根据参数类型、相应的延时时间、对应的时区放入不同的任务列表中

    protected void processScheduled(Scheduled scheduled, Method method, Object bean) {

    //获取corn类型

    String cron = scheduled.cron();

    if (StringUtils.hasText(cron)) {

    Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");

    processedSchedule = true;

    String zone = scheduled.zone();

    //放入cron任务列表中(不执行)

    this.registrar.addCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone)));

           }

    //执行频率类型(long类型)

    long fixedRate = scheduled.fixedRate();

    String fixedDelayString = scheduled.fixedDelayString();

    if (fixedRate >= 0) {

    Assert.isTrue(!processedSchedule, errorMessage);

    processedSchedule = true;

    //放入FixedRate任务列表中(不执行)(registrar为ScheduledTaskRegistrar)

    this.registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));

    }

    //执行频率类型(字符串类型,不接收参数计算如:600*20)

    String fixedRateString = scheduled.fixedRateString();

    if (StringUtils.hasText(fixedRateString)) {

    Assert.isTrue(!processedSchedule, errorMessage);

    processedSchedule = true;

    if (this.embeddedValueResolver != null) {

    fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);

           }

    fixedRate = Long.parseLong(fixedRateString);

    //放入FixedRate任务列表中(不执行)

    this.registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));

          }

        }

    return bean;

    }

    第三步:执行相应的定时任务。

    说明:定时任务先执行corn,判断定时任务的执行时间,计算出相应的下次执行时间,放入线程中,到相应的时间后进行执行。之后执行按“频率”(fixedRate)执行的定时任务,直到所有任务执行结束。

    protected void scheduleTasks() {

    //顺序执行相应的Cron

    if (this.cronTasks != null) {

    for (CronTask task : this.cronTasks) {

    this.scheduledFutures.add(this.taskScheduler.schedule(

    task.getRunnable(), task.getTrigger()));

          }

         }

    //顺序执行所有的“fixedRate”定时任务(无延迟,也就是说initialDelay参数为空),因为无延迟,所以定时任务会直接执行一次,执行任务完成后,会将下次执行任务的时间放入delayedExecute中等待下次执行。

    if (this.fixedRateTasks != null) {

    for (IntervalTask task : this.fixedRateTasks) {

    if (task.getInitialDelay() > 0) {

    Date startTime = new Date(now + task.getInitialDelay());

    this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(

    task.getRunnable(), startTime, task.getInterval()));

         }

    else {

    this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(

    task.getRunnable(), task.getInterval()));

             }

          }

        }

    //顺序执行所有的“fixedRate”定时任务(有延迟,也就是说initialDelay参数不为空)

    if (this.fixedDelayTasks != null) {

    for (IntervalTask task : this.fixedDelayTasks) {

    if (task.getInitialDelay() > 0) {

    Date startTime = new Date(now + task.getInitialDelay());

    this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(

    task.getRunnable(), startTime, task.getInterval()));

         }

    else {

    this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(

    task.getRunnable(), task.getInterval()));

             }

            }

           }

         }

    接下来看下定时任务run(extends自Runnable接口)方法:

    //说明:每次执行定时任务结束后,会先设置下下次定时任务的执行时间,以此来确认下次任务的执行时间。

    public void run() {

    boolean periodic = isPeriodic();

    if (!canRunInCurrentRunState(periodic))

    cancel(false);

    else if (!periodic)

    ScheduledFutureTask.super.run();

    else if (ScheduledFutureTask.super.runAndReset()) {

    setNextRunTime();

    reExecutePeriodic(outerTask);

            }

    }

    备注1:从上面的代码可以看出,如果多个定时任务定义的是同一个时间,那么也是顺序执行的,会根据程序加载Scheduled方法的先后来执行。

    但是如果某个定时任务执行未完成会出现什么现象呢?

    答:此任务一直无法执行完成,无法设置下次任务执行时间,之后会导致此任务后面的所有定时任务无法继续执行,也就会出现所有的定时任务“失效”现象。

    所以应用springBoot中定时任务的方法中,一定不要出现“死循环”、“http持续等待无响应”现象,否则会导致定时任务程序无法正常。再就是非特殊需求情况下可以把定时任务“分散”下。

    cron表达式

    一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。

    按顺序依次为

    秒(0~59)

    分钟(0~59)

    小时(0~23)

    天(月)(0~31,但是你需要考虑你月的天数)

    月(0~11)

    天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)

    7.年份(1970-2099)

    例子     *  *  *  *  * ?* *

    0 0 10,14,16 * * ? 每天上午10点,下午2点,4点

    0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时

    0 0 12 ? * WED 表示每个星期三中午12点

    "0 0 12 * * ?" 每天中午12点触发

    "0 15 10 ? * *" 每天上午10:15触发

    "0 15 10 * * ?" 每天上午10:15触发

    "0 15 10 * * ? *" 每天上午10:15触发

    "0 15 10 * * ? 2005" 2005年的每天上午10:15触发

    "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发

    "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发

    "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发

    "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发

    "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发

    "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发

    "0 15 10 15 * ?" 每月15日上午10:15触发

    "0 15 10 L * ?" 每月最后一日的上午10:15触发

    "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发

    "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发

    "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

    作者:谁在烽烟彼岸

    链接:https://www.jianshu.com/p/36099c453c4b

    來源:简书

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

          本文标题:spring中定时任务

          本文链接:https://www.haomeiwen.com/subject/lplmcftx.html