系统。currentTimeMillis vs System.nanoTime

准确Vs.精确

我想知道的是,我是否应该使用System.currentTimeMillis ()system . nanotime ()更新我的对象的位置在我的游戏?它们移动的变化与上次通话后经过的时间成正比,我希望尽可能精确。

我读到过不同操作系统之间有一些严重的时间分辨率问题(即Mac / Linux的分辨率几乎是1毫秒,而Windows的分辨率是50毫秒??)我主要在windows上运行我的应用程序,50ms的分辨率似乎非常不准确。

还有比我列出的两个更好的选择吗?

有什么建议/意见吗?

429923 次浏览

如果你只是在寻找运行时间的极其精确的测量值,请使用System.nanoTime()System.currentTimeMillis()将为您提供自epoch以来最精确的可能经过时间(以毫秒为单位),但System.nanoTime()将为您提供相对于某个任意点的纳秒精确时间。

摘自Java文档:

public static long nanoTime()

返回最精确的可用系统计时器的当前值(以纳秒为单位)。

此方法只能用于 测量经过的时间和不是 与系统的其他概念有关 或者是挂钟时间。返回的值 表示从some开始的纳秒 固定但任意的起源时间(可能在 未来,价值观可能就是这样 负面的)。此方法提供 纳秒的精度,但不是 必须达到纳秒精度。没有 保证是如何做到的 价值观经常变化。差异 在连续的呼叫中,跨度更大 比大约292年 纳秒)不会准确 计算由于数值导致的运行时间 溢出。< / p >

例如,要测量一些代码执行所需的时间:

long startTime = System.nanoTime();
// ... the code being measured ...
long estimatedTime = System.nanoTime() - startTime;

更多信息请参见:JavaDoc system . nanotime ()JavaDoc System.currentTimeMillis ()

旧的jvm中不支持System.nanoTime()。如果这是一个问题,坚持使用currentTimeMillis

关于准确性,你几乎是正确的。在一些Windows机器上,currentTimeMillis()的分辨率约为10ms(而不是50ms)。我不知道为什么,但是一些Windows机器和Linux机器一样准确。

我曾经使用GAGETimer在过去取得了一定的成功。

是的,如果需要这样的精度,请使用System.nanoTime(),但请注意,您需要Java 5+ JVM。

在我的XP系统上,我看到系统时间报告给至少100微秒 278 纳秒使用以下代码:

private void test() {
System.out.println("currentTimeMillis: "+System.currentTimeMillis());
System.out.println("nanoTime         : "+System.nanoTime());
System.out.println();


testNano(false);                                                            // to sync with currentTimeMillis() timer tick
for(int xa=0; xa<10; xa++) {
testNano(true);
}
}


private void testNano(boolean shw) {
long strMS=System.currentTimeMillis();
long strNS=System.nanoTime();
long curMS;
while((curMS=System.currentTimeMillis()) == strMS) {
if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
}
if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
}

Arkadiy更新:我在Oracle Java 8中观察到Windows 7上System.currentTimeMillis()的更正确的行为。时间以1毫秒的精度返回。OpenJDK中的源代码并没有改变,所以我不知道是什么原因导致了更好的行为。


Sun的David Holmes几年前发表了一篇博客文章,其中非常详细地介绍了Java计时api(特别是System.currentTimeMillis()System.nanoTime()),何时使用它们,以及它们在内部是如何工作的。

热点虚拟机内部:时钟、定时器和调度事件- Part I - Windows . sh 热点虚拟机内部:时钟、定时器和调度事件- Part I - Windows

Java在Windows上为具有定时等待参数的API使用的计时器的一个非常有趣的方面是,计时器的分辨率可以根据可能已经进行的其他API调用而改变-系统范围(不仅仅是在特定进程中)。他展示了一个使用Thread.sleep()将导致分辨率改变的例子。

我对nanotime有很好的经验。它使用JNI库提供了两个长度的挂钟时间(从纪元开始的秒数和在这一秒内的纳秒)。在Windows和Linux上都可以使用预编译的JNI部分。

既然没人提过这一点……

在不同的JVM之间比较System.nanoTime()调用的结果是不安全的,每个JVM可能有一个独立的“起源”时间。

System.currentTimeMillis()将在jvm之间返回(近似)相同的值,因为它与系统墙壁时钟时间绑定。

如果你想计算两个事件之间经过的时间,比如秒表,使用nanoTime();在这个用例中,系统时钟的改变使得currentTimeMillis()不正确。

这里的一个问题是nanoTime方法的不一致性。对于相同的输入,它不会给出非常一致的值。currentTimeMillis在性能和一致性方面做得更好,而且,尽管没有nanoTime那么精确,但它的误差范围更低,因此它的值更准确。因此,我建议您使用currentTimeMillis

正如其他人所说,currentTimeMillis是时钟时间,由于日光节约时间(不是:日光节约;如果与currentTimeMillis无关,其余为true)、用户更改时间设置、闰秒和互联网时间同步而改变。如果您的应用程序依赖于单调增加运行时间值,您可能更喜欢nanoTime。

你可能认为玩家不会在游戏过程中摆弄时间设置,也许你是对的。但不要低估互联网时间同步或远程桌面用户带来的破坏。nanoTime API不会受到这种中断的影响。

如果你想使用时钟时间,但避免由于互联网时间同步造成的不连续性,你可以考虑一个NTP客户端,如Meinberg,它可以“;时钟速率归零,而不是定期重置时钟。

这是我的亲身经历。在我开发的一个天气应用程序中,我得到了随机出现的风速峰值。我花了一段时间才意识到,我的时间库被一台典型PC上的时钟时间行为打乱了。当我开始使用nanoTime时,我所有的问题都消失了。对于我的应用程序来说,一致性(单调性)比原始精度或绝对精度更重要。

游戏图形&平滑位置更新,使用System.nanoTime()而不是System.currentTimeMillis()。我在游戏中从currentTimeMillis()切换到nanoTime(),在运动的平滑度上有了很大的视觉改善。

虽然1毫秒看起来已经很精确了,但从视觉上看并非如此。nanoTime()可以改善的因素包括:

  • 精确的像素定位低于时钟分辨率
  • 如果你想要,像素之间的抗别名能力
  • 窗户挂钟不准
  • 时钟抖动(当挂钟实际滴答前进时不一致)

正如其他答案所表明的那样,如果重复调用nanoTime,确实会有性能损失——最好每帧只调用一次,并使用相同的值来计算整个帧。

System.currentTimeMillis()对于经过的时间是不安全的,因为这个方法对系统实时时钟的变化很敏感。 你应该使用System.nanoTime。 请参考Java系统帮助:

关于nanoTime方法:

< p > . .这种方法提供纳秒精度,但不一定 纳秒分辨率(即值变化的频率)—不 除了保证分辨率至少和 currentTimeMillis()..

如果你使用System.currentTimeMillis(),你的运行时间可以是负数(Back <- to future)