System.currentTimeMillis vs System.nanoTime

mmc*_*ole 370 java timer time-precision

精度与 精确

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

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

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

有什么建议/意见吗?

dan*_*aro 311

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

从Java文档:

public static long nanoTime()
Run Code Online (Sandbox Code Playgroud)

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

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关.返回的值表示纳秒,因为某些固定但是任意时间(可能在将来,因此值可能为负).该方法提供纳秒精度,但不一定是纳秒精度.不保证值的变化频率. 由于数值溢出,跨越大于约292年(2 63纳秒)的连续调用的差异将无法准确计算经过的时间.

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

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;
Run Code Online (Sandbox Code Playgroud)

另请参阅:JavaDoc System.nanoTime()JavaDoc System.currentTimeMillis()以获取更多信息.

  • 这个答案在选择nanoTime()时在技术上是正确的,但完全掩盖了极其重要的一点.正如文档所说,nanoTime()是一个精确计时器.currentTimeMillis()不是定时器,它是"挂钟".nanoTime()将始终产生正的经过时间,currentTimeMillis将不会(例如,如果您更改日期,点击闰秒等).对于某些类型的系统,这是一个非常重要的区别. (129认同)
  • 你确定你知道准确度和精度之间的区别吗?它不可能精确到纳秒精度. (20认同)
  • 用户更改时间和NTP同步确定,但为什么`currentTimeMillis`会因DST而改变?DST开关不会更改超过纪元的秒数​​.它可能是"挂钟",但它是基于UTC的挂钟.您必须根据您的时区和DST设置确定当地时间转换的内容(或使用其他Java实用程序为您执行此操作). (10认同)
  • 对不起,我的意思很精确.我使用松散的术语,但我同意这是令人困惑的(并且不正确地使用了这个词). (5认同)
  • @dancavallaro,感谢您提供的信息.如果您不介意,我编辑了您的答案,以包含文档中的引用并修复链接 (4认同)
  • 连续的电话,大约超过292年....我的新报价! (2认同)

gub*_*bby 93

既然没有人提到过这个......

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

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

  • 看看这里提到的描述:http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime(),看来nanoTime返回的值之间的差异是有效的只要它们来自同一个JVM就可以进行比较 - 只有当计算出在Java虚拟机的同一个实例中获得的两个这样的值之间的差异时,此方法返回的值才有意义. (12认同)
  • [JavaDoc for`nanoTime()`](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#nanoTime--)说:*使用相同的来源在Java虚拟机的实例中调用此方法的所有内容; 其他虚拟机实例可能使用不同的来源.*这意味着它**会在线程中返回相同的内容. (5认同)
  • 在根据以下内容仅保留到SP2的Windows上:http://stackoverflow.com/questions/510462/is-system-nanotime-completely-useless (4认同)
  • @jgubby:非常有趣......**任何对支持的引用_不能安全地比较不同线程之间的System.nanoTime()调用结果_?**以下链接值得一看:http://bugs.sun .com/bugdatabase/view_bug.do?bug_id = 6519418 http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime() (4认同)
  • 好吧,你每天都学到新东西.但我怀疑,鉴于它过去是不安全的(肯定会返回跨线程的无意义读数),这种用法可能仍然在规范之外,因此应该避免. (3认同)
  • 这不完全正确,nanoTime()在线程之间一致返回,除非您的系统有问题,这个数字将在调用之间按预期增加.另一方面,System.currentTimeMillis()甚至可以在同一个线程上向后跳转,因为它代表了挂起时间,可以通过外部因素(如NTP更新)进行调整. (3认同)
  • 我认为没有任何理由首先测量不同线程之间的时间.但不管怎样,我想在你的声明中添加一个"引用",即"System.nanoTime()"不是线程安全的. (3认同)
  • 这个答案是错误的。情况恰好相反:currentTimeMillis()也不是线程安全的(假定的含义,即单调非递减结果),即使在单个线程中也是如此。nanoTime()是线程安全的。 (2认同)

Mic*_*urr 58

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


孙大卫霍姆斯发布了博客文章一对夫妇多年前拥有的Java API的时机非常详细的外观(尤其是System.currentTimeMillis()System.nanoTime()),当你想使用的,以及它们如何在内部工作.

Hotspot VM内部:时钟,定时器和调度事件 - 第一部分 - Windows

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

  • 我将currentTimeMillis跟踪到OpenJDK中的hotspot/src/os/windows/vm/os_windows.cpp中的os :: javaTimeMillis(http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/ OS /窗/ VM/os_windows.cpp#l833).看起来它仍然是GetSystemTimeAsFileTime,所以我不知道改变的来源.或者,如果它甚至有效.使用前测试. (2认同)

Pau*_*rel 11

System.nanoTime()旧JVM不支持.如果这是一个问题,坚持下去currentTimeMillis

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

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

  • "旧JVM"和哪些一样?Java 1.2还是什么? (3认同)
  • System.nanoTime() 于 2004 年在 Java 1.5 中引入。Java 1.4 在 2013 年扩展了支持,因此可以肯定地说 System.nanoTime() 现在可以信赖,并且这个答案现在已经过时了。 (2认同)

Kar*_*rlU 9

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

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

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

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

  • *"currentTimeMillis是时钟时间,由于夏令时而改变"*...非常确定此语句是错误的.`System.currentTimeMillis()`报告自Unix/Posix Epoch(1970年1月1日午夜)以来的经过时间(以毫秒为单位).由于UTC永远不会针对夏令时进行调整,因此当本地时区为夏令时添加或偏移当地时间时,此值不会被抵消.此外,Java Time Scale平滑了闰秒,因此几天继续显示为86,400秒. (18认同)

Law*_*Dol 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)); }
    }
Run Code Online (Sandbox Code Playgroud)


Tho*_*s W 5

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

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

  • 低于挂钟分辨率的精确像素定位
  • 如果需要的话,能够在像素之间消除锯齿
  • Windows 挂钟不准确
  • 时钟抖动(挂钟实际向前滴答作响的时间不一致)

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