Java中的ScheduledExecutorService的时间一致性问题

时间:2016-01-15 01:42:03

标签: java scheduledexecutorservice

我尝试使用ScheduledExecutorService每15分钟调用一次可运行的任务。这些任务应该在以下时间每小时运行一次: 第二分钟第10秒 第17分钟第10秒 第32分钟第10秒 第47分钟第10秒。

我使用Calendar实例计算首次执行任务的初始延迟,然后使用scheduleAtFixedRate()的延迟每15分钟执行一次任务。这在前几天工作正常,但几天之后我可以观察到任务执行的一些时间变化。例如,应该在12:03:45开始调用应该在12:03:45开始的任务。我在哪里错了?

public static void main(String[] args) {    
  final ScheduledExecutorService scheduler = Executors
                .newScheduledThreadPool(15);
  Date aDate = new Date();
  Calendar cal = Calendar.getInstance();
  cal.setTime(aDate);
  int currentHour = cal.get(Calendar.HOUR_OF_DAY);
  int currentMins = cal.get(Calendar.MINUTE);
  int currentSecs = cal.get(Calendar.SECOND);
  int unitInSecs;      
  int delayFor15MinTasksInSecs;
  unitInSecs = currentMins * 60 + currentSecs;
  delayFor15MinTasksInSecs = unitInSecs < (3 * 60 - 50 ) ? (3 * 60 - 50 ) - unitInSecs : unitInSecs < (18 * 60 - 50 ) ? (18 * 60 - 50 ) - unitInSecs : unitInSecs < (33 * 60 - 50 ) ? (33 * 60 - 50 ) - unitInSecs : unitInSecs < (48 * 60 - 50 )? (48 * 60 - 50 ) - unitInSecs : (63 *60 - 50 ) - unitInSecs;
  scheduler.scheduleAtFixedRate(new RTPAcquiringTask(10), delayFor15MinTasksInSecs, 15*60, TimeUnit.SECONDS);
}

2 个答案:

答案 0 :(得分:4)

此API在计时方面并不完美。

来自ScheduledExecutorService的Javadoc:

  

请注意,由于网络时间同步协议,时钟漂移或其他因素,相对延迟的到期不一定与启用任务的当前日期一致。

答案 1 :(得分:1)

非实时

正如correct Answer by Riaz及其评论所说,Oracle’sOpenJDK项目等传统Java实现不是real-time systems。所以他们没有完美的时间。诸如ScheduledExecutorService之类的课程并非旨在完美地保持时间。如果需要,请查看制作real-time Java的尝试。

解决方法

我没有尝试过这种解决方法来减少漂移;只是一个想法。

作为一种解决方法,您可以拥有一个额外的后台线程,其工作是偶尔终止您当前的ScheduledExecutorService工作,并开始重新计算延迟编号的新工作。你说漂移在几天之后变得明显,所以也许这个终止和重新安排的家务活每天运行一次。

您可以通过捕获ScheduledExecutorService调用返回的ScheduledFuture对象来跟踪和终止该scheduleAtFixedRate工作。目前您的调用忽略并丢失该返回的对象。跟踪它,让它可用​​于我建议的后台线程。请致电cancel以终止。