Spring Quartz执行时间

时间:2017-10-23 12:16:55

标签: spring quartz

我已经设置了一个带有以下表达式的spring quertz计时器:

@Scheduled(cron = "${quartz.expire.data.cron:0 0 0 * * ?}")

但它开始有点提前,如我们的日志所示:

2017-10-22 23:59:59.899 scheduler-4

为什么?

1 个答案:

答案 0 :(得分:1)

这可能来自下一个执行时间的计算。

ReschedulingRunnable的{​​{3}}方法中,时间取自第68行的nextExecutionTime。执行的实际延迟在第72行计算。

66  public ScheduledFuture<?> schedule() {
67      synchronized (this.triggerContextMonitor) {
68          this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
69          if (this.scheduledExecutionTime == null) {
70              return null;
71          }
72          long initialDelay = this.scheduledExecutionTime.getTime() - System.currentTimeMillis();
73          this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
74          return this;
75      }
76  }

现在让我们看看schedulenextExecutionTime方法中发生了什么:

72  @Override
73  public Date nextExecutionTime(TriggerContext triggerContext) {
74      Date date = triggerContext.lastCompletionTime();
75      if (date != null) {
76          Date scheduled = triggerContext.lastScheduledExecutionTime();
77          if (scheduled != null && date.before(scheduled)) {
78              // Previous task apparently executed too early...
79              // Let's simply use the last calculated execution time then,
80              // in order to prevent accidental re-fires in the same second.
81              date = scheduled;
82          }
83      }
84      else {
85          date = new Date();
86      }
87      return this.sequenceGenerator.next(date);
88  }

在第86行,需要时间。所采用的时刻是计算CronTrigger

中发生的cron定义的下一个执行时间的基础
134     Calendar calendar = new GregorianCalendar();
135     calendar.setTimeZone(this.timeZone);
136     calendar.setTime(date);
137
138     // First, just reset the milliseconds and try to calculate from there...
139     calendar.set(Calendar.MILLISECOND, 0);
140     long originalTimestamp = calendar.getTimeInMillis();
141     doNext(calendar, calendar.get(Calendar.YEAR));
142
143     if (calendar.getTimeInMillis() == originalTimestamp) {
144         // We arrived at the original timestamp - round up to the next whole second and try again...
145         calendar.add(Calendar.SECOND, 1);
146         doNext(calendar, calendar.get(Calendar.YEAR));
147     }
148
149     return calendar.getTime();

这肯定需要几毫秒,在initialDelay中将会丢失。

<强>证明

一个小小的测试证明这看起来像下面。我创建了一个常规CronTrigger和一个被操纵的CronTrigger

@Test
public void test() {
    CronTrigger normalTrigger= new CronTrigger("0 0 0 * * ?");
    Date d2 = normalTrigger.nextExecutionTime(new SimpleTriggerContext());
    long initialDelay2 = d2.getTime() - System.currentTimeMillis();
    System.out.println("Normal trigger:"+ initialDelay2);

    //create a manipulated trigger, which takes longer to return the nextExecutionTime
    CronTrigger manipulated = new CronTrigger("0 0 0 * * ?") {
        @Override
        public Date nextExecutionTime(TriggerContext triggerContext) {
            Date nextExecutionTime = super.nextExecutionTime(triggerContext);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
              //ignore
            }
            return nextExecutionTime;
        }
    };
    Date d = manipulated.nextExecutionTime(new SimpleTriggerContext());
    long initialDelay = d.getTime() - System.currentTimeMillis();
    System.out.println("Manipulated trigger:" +initialDelay);
}

从结果中可以看出,被操纵的触发器将比非操纵触发器提前5秒触发,因为返回nextExecutionTime需要5秒钟。