System.currentTimeMillis vs System.nanoTime

时间:2008-12-09 01:58:48

标签: java timer time-precision

准确度与精度

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

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

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

有任何建议/意见吗?

10 个答案:

答案 0 :(得分:302)

如果您只是想要非常精确地测量经过时间,请使用System.nanoTime()System.currentTimeMillis()将为您提供自纪元以来最准确的可用时间(以毫秒为单位),但System.nanoTime()为您提供相对于某个任意点的纳秒精确时间。

来自Java文档:

public static long nanoTime()
     

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

     

此方法只能用于   测量经过的时间而不是   与任何其他系统概念有关   或挂钟时间。返回的值   从某些方面起代表纳秒   固定但任意的时间(也许是在   未来,所以价值观可能如此   负)。这种方法提供   纳秒精度,但不是   必须达到纳秒精度。没有   保证是如何做的   经常改变价值观。差异   在连续的呼叫中跨越更大   大约292年(2 63   纳秒)不准确   计算由数字引起的经过时间   溢出。

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

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

另请参阅:JavaDoc System.nanoTime()JavaDoc System.currentTimeMillis()了解详情。

答案 1 :(得分:88)

因为没有人提到过这个......

比较不同线程之间System.nanoTime()次调用的结果是不安全的。即使线程的事件以可预测的顺序发生,纳秒的差异也可以是正的或负的。

System.currentTimeMillis()在线程之间使用是安全的。

答案 2 :(得分:58)

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


几年前,Sun的David Holmes发布了一篇博客文章,其中详细介绍了Java时序API(特别是System.currentTimeMillis()System.nanoTime()),当你想要使用它时,以及他们如何在内部工作。

Inside the Hotspot VM: Clocks, Timers and Scheduling Events - Part I - Windows

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

答案 3 :(得分:10)

较旧的JVM不支持

System.nanoTime()。如果这是一个问题,请坚持currentTimeMillis

关于准确性,你几乎是正确的。在某些Windows计算机上,currentTimeMillis()的分辨率约为10毫秒(不是50毫秒)。我不确定为什么,但有些Windows机器和Linux机器一样准确。

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

答案 4 :(得分:8)

正如其他人所说,currentTimeMillis是时钟时间,由于夏令时,用户更改时间设置,闰秒和互联网时间同步而改变。如果您的应用依赖于单调增加的经过时间值,您可能更喜欢nanoTime。

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

如果您想使用时钟时间,但避免因互联网时间同步导致的不连续性,您可能会考虑使用一个NTP客户端,例如Meinberg,它将时钟频率“调整”为零,而不是仅定期重置时钟。

我是根据个人经验说的。在我开发的天气应用中,我随机发生风速峰值。我花了一段时间才意识到我的时基被典型PC上的时钟时间行为所扰乱。当我开始使用nanoTime时,我的所有问题都消失了。对于我的应用,一致性(单调性)比原始精度或绝对精度更重要。

答案 5 :(得分:5)

是的,如果需要这样的精度,请使用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)); }
    }

答案 6 :(得分:2)

我对nanotime有很好的经验。它使用JNI库提供挂钟时间为两个长(自纪元以来的秒数和在该秒内的纳秒)。它适用于Windows和Linux预编译的JNI部分。

答案 7 :(得分:2)

对于游戏图形&amp;平滑的排名更新,请使用System.nanoTime()而不是System.currentTimeMillis()。我在游戏中从currentTimeMillis()切换到nanoTime(),并在运动的平滑性方面获得了重大的视觉改进。

虽然一毫秒看起来应该已经很精确,但在视觉上却不是这样。 nanoTime()可以改善的因素包括:

  • 精确的像素定位低于挂钟分辨率
  • 如果您想要
  • ,则可以在像素之间进行消除锯齿
  • Windows挂钟不准确
  • 时钟抖动(当挂钟实际向前嘀嗒时不一致)

正如其他答案所示,如果重复调用nanoTime确实会产生性能成本 - 最好每帧调用一次,并使用相同的值来计算整个帧。

答案 8 :(得分:0)

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

关于nanoTime方法:

  

..此方法提供纳秒精度,但不一定   纳秒分辨率(即,值的变化频率) - 否   除了分辨率至少与分辨率一样好之外,还要做出保证   currentTimeMillis()..

如果你使用System.currentTimeMillis(),你的经过时间可能是负数(返回&lt; - 到将来)

答案 9 :(得分:-4)

这里有一点是nanoTime方法的不一致性。对于相同的输入,它没有给出非常一致的值。currentTimeMillis在性能和一致性方面做得更好,而且虽然不像nanoTime那样精确,但是误差范围,因此其值更准确。因此我建议您使用currentTimeMillis