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()
会导致此解决scheme更改的示例。
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系统中,使用以下代码将系统时间报告为至less100微秒 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部分中使用。
对于游戏graphics和平滑的位置更新,请使用System.nanoTime()
而不是System.currentTimeMillis()
。 我在游戏中从currentTimeMillis()切换到了nanoTime(),并且在运动平滑度方面获得了重大的视觉改善。
虽然一毫秒可能看起来应该已经是精确的,但在视觉上它不是。 nanoTime()
可以改进的因素包括:
- 准确的像素定位低于壁钟分辨率
- 如果需要,可以在像素之间进行反锯齿
- Windows墙上的时钟不准确
- 时钟抖动(当时钟实际上向前跳动时的不一致)
正如其他答案所暗示的,如果重复调用,nanoTime确实会有性能成本 – 最好每帧调用一次,并使用相同的值来计算整个帧。
这里有一件事是nanoTime方法的不一致性。对于相同的input,它没有给出非常一致的值。在性能和一致性方面,currentTimeMillis做得更好,而且虽然不如nanoTime精确,但具有较低的误差,因此其价值更准确。 我会因此build议你使用currentTimeMillis