为了进行诊断,我希望能够在长时间运行的服务器应用程序中检测系统时钟的变化。由于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的机制来调整系统时钟速度,以便将时钟时钟慢慢地与时钟源同步?这样的事情会影响定时器,还是只影响挂钟计时器?
(*)我明白,在某些架构上,System.nanoTime()必须使用与System.currentTimeMillis()相同的机制。我还认为,任何现代Windows服务器都不是这样的硬件架构是公平的。这是不好的假设吗?
(**)当然,System.currentTimeMillis()通常会比System.nanoTime()大得多的抖动,因为它的粒度在大多数系统上不是1毫秒。