为什么System.nanoTime()和System.currentTimeMillis()相差如此之快?
出于诊断目的,我希望能够在长时间运行的服务器应用程序中检测系统时间时钟的变化。由于 它 基于挂钟时间,并且基于独立于挂钟时间的系统计时器,因此我认为可以使用这些值之间的差异变化来检测系统时间变化。System.currentTimeMillis()
System.nanoTime()
我编写了一个快速测试应用程序,以查看这些值之间的差异有多稳定,令我惊讶的是,这些值立即以每秒几毫秒的水平发散。有几次我看到了更快的背离。这是在带有Java 6的Win7 64位桌面上。我还没有在Linux(或Solaris或MacOS)下尝试下面的测试程序,看看它的性能如何。对于此应用的某些运行,散度为正,对于某些运行,背离为负。这似乎取决于桌面正在做什么,但很难说。
public class TimeTest {
private static final int ONE_MILLION = 1000000;
private static final int HALF_MILLION = 499999;
public static void main(String[] args) {
long start = System.nanoTime();
long base = System.currentTimeMillis() - (start / ONE_MILLION);
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Don't care if we're interrupted
}
long now = System.nanoTime();
long drift = System.currentTimeMillis() - (now / ONE_MILLION) - base;
long interval = (now - start + HALF_MILLION) / ONE_MILLION;
System.out.println("Clock drift " + drift + " ms after " + interval
+ " ms = " + (drift * 1000 / interval) + " ms/s");
}
}
}
时间的不准确以及中断应该与计时器漂移完全无关。Thread.sleep()
这两个Java“系统”调用都旨在用作测量 - 一个用于测量挂钟时间的差异,另一个用于测量绝对间隔,因此当实时时钟未更改时,这些值应以非常接近相同的速度变化,对吗?这是Java中的错误,弱点还是失败?操作系统或硬件中是否有某些东西阻止了Java更准确?
我完全预计这些独立测量之间会有一些漂移和抖动(**),但我预计每天的漂移时间不到一分钟。每秒1毫秒的漂移,如果单调,几乎是90秒!我观察到的最坏情况漂移可能是这个数字的十倍。每次运行此程序时,我都会在第一次测量时看到漂移。到目前为止,我还没有运行该程序超过30分钟。
由于抖动,我期望在打印的值中看到一些小的随机性,但是在几乎所有的程序运行中,我都看到了差异的稳步增加,通常高达每秒3毫秒的增加,并且比这多几倍。
是否有任何版本的Windows具有类似于Linux的机制来调整系统时钟速度,以缓慢地使时间时钟与外部时钟源同步?这样的事情会影响两个计时器,还是只影响挂钟计时器?
(*)我理解,在某些体系结构上,必然会使用与 相同的机制。我也认为,假设任何现代Windows服务器都不是这样的硬件架构是公平的。这是一个糟糕的假设吗?System.nanoTime()
System.currentTimeMillis()
(**)当然,通常具有比大多数系统上的粒度大得多的抖动,因为它的粒度不是1毫秒。System.currentTimeMillis()
System.nanoTime()