我已经设置了一个带有以下表达式的spring quertz计时器:
@Scheduled(cron = "${quartz.expire.data.cron:0 0 0 * * ?}")
但它开始有点提前,如我们的日志所示:
2017-10-22 23:59:59.899 scheduler-4
为什么?
答案 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 }
现在让我们看看schedule
的nextExecutionTime
方法中发生了什么:
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
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秒钟。