Java 中 ScheduledExecutorService 的时间一致性问题

Time consistency issue with ScheduledExecutorService in Java

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

我使用 Calendar 实例计算第一次执行任务的初始延迟,然后使用 scheduleAtFixedRate() 的延迟每 15 分钟执行一次任务。这在最初几天工作正常,但几天后我可以观察到任务执行的时间偏移。例如,本应在 12:02:10 开始的任务却在 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);
}

API 在 time-keeping 中并不完美。

来自 ScheduledExecutorService 的 Javadoc:

Beware however that expiration of a relative delay need not coincide with the current Date at which the task is enabled due to network time synchronization protocols, clock drift, or other factors.

不是Real-Time

作为 and its comments say, conventional Java implementations such as Oracle’s or the OpenJDK project are not real-time systems. So they do not keep time perfectly. Classes such as ScheduledExecutorService are not intended to keep time perfectly. If need be, look into the attempts at making a real-time Java.

解决方法

我还没有尝试过这个解决方法来最小化偏差;只是一个想法。

作为一种解决方法,您可以拥有一个额外的后台线程,其工作是偶尔终止您当前的 ScheduledExecutorService 工作,并开始一个具有 re-calculated 延迟数的新工作。你说漂移在几天后变得明显,所以也许 terminate-and-reschedule 每天做一次苦差事 运行。

您可以通过捕获 ScheduledFuture object being returned by your call to scheduleAtFixedRate. Currently your call ignores and loses that returned object. Keep track of it, make it available to the background thread I suggest. Call cancel 来跟踪和终止 ScheduledExecutorService 工作。