Java预定执行程序的准确性

我在使用Java预定执行程序时遇到了一个特殊问题,并且想知道我所经历的是否正常。

我需要安排以5秒的预定义速率执行的任务。 预计这些任务将不时需要超过5秒的时间执行,但是当运行它们的时间低于5秒时,备份的任务列表应该快速连续运行以赶上。 在运行任务时,重要的是要知道原始计划执行时间是什么(想想java.util.TimerTask scheduledExecutionTime() )。 最后,我需要跟踪预定时间和实际时间之间的差异,以确定时间表“漂移”的时间和数量。

到目前为止,我已经使用Java执行器实现了所有这些,下面的类说明了一般的想法:

 public class ExecutorTest { public static final long PERIOD = 5000; public static void main(String[] args) { Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate( new Command(), 0, PERIOD, TimeUnit.MILLISECONDS); } private static final class Command implements Runnable { long timestamp = 0; public void run() { long now = System.currentTimeMillis(); if (timestamp == 0) { timestamp = now; } // Drift is the difference between scheduled time and execution time long drift = now - timestamp; String format = "Ran at %1$tF %<tT,%<tL; drift: %2$dms"; System.out.println(String.format(format, now, drift)); timestamp += PERIOD; } } } 

运行上面列出的代码表明漂移(理想情况下应尽可能接近0)波动多达几秒钟,其结果是任务过早或过晚执行。 我已经根据运行结果创建了一个图表大约150分钟:

Java执行器漂移

所以我的第一个问题是这是否正常。 我的环境包括32位Windows XP和Java 1.5更新21(尽管Java 6更新22产生类似的结果)。

第二个问题是,是否有一种简单的方法可以减少漂移量。 如果我使用简单的java.util.Timer或甚至只是Thread.sleep() ,则漂移不存在。

最后,在使用预定执行程序时,是否有更好的方法来跟踪计划的执行时间?

计划的执行程序服务使用System.nanoTime,它不会像currentTimeMillis那样漂移。 除非您在具有多个CPU插槽的XP系统上运行。 操作系统调用System.nanoTime()使用的操作系统中的一个错误是套接字之间不一致,因此当线程切换它运行的套接字时,您可以期待看到这种情况。 (这不是Vista / 7上的问题)

在具有一个插槽的Linux系统上,程序报告0 – 3 ms漂移。

试试这个程序。

 public static void main(String... args) throws Exception { long start = System.nanoTime(); long time = start; while(time < start + 3e10) { long now = System.nanoTime(); if (now < time || now > time + 50000) { System.out.println(now - time); now = System.nanoTime(); } time = now; } } 

在i7系统上,我看到大约10次跳跃,最多2 ms。 如果我使用机器,我会看到更多。 我期望你看到的是大的消极和积极的时间。

对我来说似乎很正常,它在5000/2的范围内变化。 您应该尝试快速记录,而不是格式+ println,因此不会测量println的开销漂移。 会很有趣。