System.currentTimeMillis与System.nanoTime

精度与VS 精确

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

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

有比我列出的两个更好的select吗?

任何build议/意见?

如果您只是在寻找非常精确的测量时间 ,请使用System.nanoTime()System.currentTimeMillis()将为您提供从历元以来最精确的可能耗用时间(以毫秒为单位System.nanoTime() ,但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()以获取更多信息。

线程安全

由于没有人提到这个…

不安全

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

安全

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

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


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

在热点虚拟机内部:时钟,定时器和调度事件 – 第一部分 – Windows

Windows上的Java对于具有定时等待参数的API使用的定时器的一个非常有趣的方面是定时器的分辨率可以改变,取决于可能已经做出的其他API调用 – 系统范围(不仅在特定过程中) 。 他显示了使用Thread.sleep()会导致此解决方案更改的示例。

System.nanoTime()在旧的JVM中不受支持。 如果这是一个问题,坚持currentTimeMillis

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

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

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

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

如果你想使用时钟时间,但避免由于互联网时间同步造成的不连续性,你可能会考虑一个像Meinberg这样的NTP客户端,它可以“调整”时钟频率来使其归零,而不是只是周期性地重置时钟。

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

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

我已经有了很好的经验。 它使用一个JNI库,提供了两个长时间(从第二个秒到第二个秒)的挂钟时间。 它可以在为Windows和Linux预编译的JNI部分中使用。

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

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

  • 准确的像素定位低于壁钟分辨率
  • 如果需要,可以在像素之间进行反锯齿
  • Windows墙上的时钟不准确
  • 时钟抖动(当时钟实际上向前跳动时的不一致)

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

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

关于nanoTime方法:

..这种方法提供了纳秒精度,但不一定是毫微秒分辨率(也就是说值的变化频率) – 除了分辨率至少与currentTimeMillis()的分辨率一样好之外,不能做出任何保证。

如果使用System.currentTimeMillis()所用时间可能为负(返回< – 将来)

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

关于nanoTime方法:

..这种方法提供了纳秒精度,但不一定是毫微秒分辨率(也就是说值的变化频率) – 除了分辨率至少与currentTimeMillis()的分辨率一样好之外,不能做出任何保证。

如果使用System.currentTimeMillis()所用时间可能为负(返回< – 将来)

这里有一件事是nanoTime方法的不一致性。对于相同的输入,它没有给出非常一致的值。在性能和一致性方面,currentTimeMillis做得好得多,并且虽然不如nanoTime精确,但具有较低的误差,因此其价值更准确。 我会因此建议你使用currentTimeMillis