代码之家  ›  专栏  ›  技术社区  ›  shantanu

Java中ScheduledExecutorService的时间一致性问题

  •  2
  • shantanu  · 技术社区  · 10 年前

    我正在尝试使用ScheduledExecutorService每15分钟调用一次可运行任务。任务应在以下时间每小时运行一次: 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);
    }
    
    2 回复  |  直到 10 年前
        1
  •  5
  •   Basil Bourque    10 年前

    这个API在计时方面并不完美。

    来自的Javadoc ScheduledExecutorService :

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

        2
  •  1
  •   Community Mohan Dere    9 年前

    非实时

    作为 correct Answer by Riaz 其评论称,传统的Java实现,如 Oracle’s 或 OpenJDK 项目不是 real-time systems 。所以他们没有完美地把握时间。类,如 ScheduledExecutorService 并不是为了完美地保持时间。如果需要的话,请查看 real-time Java .

    解决方法

    我没有尝试过这种变通方法来尽量减少漂移;只是一个想法。

    作为一种变通方法,您可以有一个额外的后台线程,其任务是偶尔终止当前 计划执行器服务 工作,并用重新计算的延迟数开始新的工作。你说,几天后,这种变化就变得明显了,所以也许可以终止这种情况,每天重新安排一次家务。

    你可以跟踪并终止 计划执行器服务 通过捕获 ScheduledFuture 调用返回的对象 scheduleAtFixedRate 。当前您的调用忽略并丢失返回的对象。跟踪它,使它可用于我建议的后台线程。呼叫 cancel 终止。