2009-10-14 79 views
2

我的一個朋友向我展示了他所做的一些事情,並且我非常遺憾地解釋了這可能發生的情況:他使用System.nanotime來計時,並且每隔一秒給用戶一次更新以告知已經過了多少時間(Thread.sleep(1000)),並且它看起來是永遠的(等待10秒鐘需要大約3分鐘才能完成)。我們嘗試使用millitime來觀察已經過了多少時間:它每秒都會顯示已經經過了多少納米量級,而且我們看到,每秒鐘,納米級每秒鐘移動大約40-50毫秒。System.nanotime運行緩慢嗎?

我檢查了與System.nanotime和Java有關的錯誤,但它似乎是我能找到的唯一的東西,其中包括突然出現的節點greatly increasing and then stopping。我還瀏覽了this blog entry,這是基於我在另一個問題中讀到的內容,但沒有任何可能導致它的事情。

顯然這可以通過使用millitime來解決這種情況;這裏有很多解決方法,但是我很好奇的是,除了系統時鐘或者至少是CPU最精確的時鐘之外,還有其他問題(因爲這就是System.nanotime似乎使用的)這可能導致它像這樣一直運行緩慢?

long initialNano = System.nanoTime(); 
long initialMili = System.currentTimeMillis(); 
//Obviously the code isn't actually doing a while(true), 
//but it illustrates the point 
while(true) { 
    Thread.sleep(1000); 
    long currentNano = System.nanoTime(); 
    long currentMili = System.currentTimeMillis(); 
    double secondsNano = ((double) (currentNano - initialNano))/1000000000D; 
    double secondsMili = ((double) (currentMili - initialMili))/1000D; 
    System.out.println(secondsNano); 
    System.out.println(secondsMili); 
} 

secondsNano會沿着打印0.04線的東西,而secondsMili將打印的東西非常接近1

它看起來像沿着這條線已經報道Sun's bug database一個錯誤,但他們關閉它一個重複的,但他們的鏈接不會去現有的錯誤。它似乎是非常系統特定的,所以我越來越確信這是一個硬件問題。

+1

您可以提供有關您的環境的更多信息:操作系統,硬件,Java版本?我只是在我的機器上運行了你的樣品,它產生了預期的結果(納秒和毫秒每秒增加1,或多或少)。我的環境:Windows XP,Dell Optiplex,Sun JDK 1.6.0_14 – 2009-10-14 19:38:28

+0

不是我的機器,所以我會盡我所能:Windows XP SP3,Core Duo,JDK 1.6。我的猜測是,無論用什麼java來計算nanoTime,這可能都是一個硬件問題,但我試圖看看是否還有其他可能導致它的東西。 – DivineWolfwood 2009-10-14 20:50:49

+0

我試過你的代碼(在Win XP,Core 2 Duo,Sun Java 1.6.0_16上),但是我不能重現這個問題。在我的系統中,納秒/毫秒的時間在0.001秒內。 – Jesper 2009-10-15 08:26:18

回答

4

......他用System.nanotime使程序做某件事之前要等待,...

你能告訴我們一些代碼,演示正是他在做什麼?是不是有些奇怪的那種繁忙的循環中,像這樣:

long t = System.nanoTime() + 1000000000L; 
while (System.nanoTime() < t) { /* do nothing */ } 

如果是的話,那麼這是不是讓你的程序暫停了一段時間的正確方法。改用Thread.sleep(...)使程序等待指定的毫秒數。

+0

+1。不要忙 - 等。 – 2009-10-14 18:04:18

+0

這不是忙碌 - 等待:我將使用代碼示例編輯問題,以更明確地瞭解究竟發生了什麼。 – DivineWolfwood 2009-10-14 18:16:41

+0

想想更多,即使是在忙着等待,我們仍然會在這裏看到相同的行爲。我知道忙等待是不對的,但我想知道除了硬件故障之外是否還有其他可能導致上述一致性差異的問題。 – DivineWolfwood 2009-10-14 18:33:46

3

你知道你正在使用的循環並不需要精確的1秒來運行嗎?首先Thread.sleep()不能保證是準確的,循環中的其餘代碼確實需要一些時間來執行(根據底層實現,nanoTime()和currentTimeMillis()實際上可能會很慢)。其次,System.currentTimeMillis()也不能保證準確(它只在某些操作系統和硬件組合上每隔50ms更新一次)。你也提到它不準確到40-50ms以上,然後繼續說0.004s,實際上只有4ms。

我會建議你改變你的System.out.println()是:

System.out.println(secondsNano - secondsMili); 

這樣一來,你就可以看到這兩個時鐘多少差異了第二逐秒基礎。我在筆記本電腦上運行了大約12個小時,超過了1.46秒(快,不慢)。這表明這兩個時鐘有一些漂移。

我認爲currentTimeMillis()方法可以在很長一段時間內提供更準確的時間,但是nanoTime()具有更高的分辨率,並且適用於計時代碼或在短時間段內提供亞毫秒定時。

+0

我明白它並不需要一秒鐘才能運行。然而,對於本質上相同的時間段,大約40-50ms的兩個讀數之一已經過去了,而另一個時鐘讀出大約1000ms已經過去了。這是一致的,我不能在任何其他系統上重現它。在這一點上,我基本上已經準備好將它歸檔到一些奇怪的操作系統/ BIOS /硬件問題,因爲我不認爲我可以找到任何其他原因來解釋這一點。 – DivineWolfwood 2009-11-21 00:14:16

+0

我也在原始文章中更正了.004:我的意思是說已經註冊了0.04秒。感謝您的支持。 – DivineWolfwood 2009-11-21 00:16:25

1

我遇到過同樣的問題。除了我的情況,它更明顯。

有了這個簡單的程序:

public class test { 
    public static void main(String[] args) { 
     while (true) { 
      try { 
       Thread.sleep(1000); 
      } 
      catch (InterruptedException e) { 
      } 

      OStream.out("\t" + System.currentTimeMillis() + "\t" + nanoTimeMillis()); 
     } 
    } 

    static long nanoTimeMillis() { 
     return Math.round(System.nanoTime()/1000000.0); 
    } 
} 

我得到如下結果:

13:05:16:380 main: 1288199116375 61530042 
13:05:16:764 main: 1288199117375 61530438 
13:05:17:134 main: 1288199118375 61530808 
13:05:17:510 main: 1288199119375 61531183 
13:05:17:886 main: 1288199120375 61531559 

的nanoTime是隻顯示400毫秒〜經過每個第二。