1. 程式人生 > >Java中的時間精度 .System.currentTimeMillis() 、System.nanoTime() 方法 使用

Java中的時間精度 .System.currentTimeMillis() 、System.nanoTime() 方法 使用

 在Java中可以通過System.currentTimeMillis()或者System.nanoTime() (JDK>=5.0) 方法獲得當前的時間的精確值。但是通過閱讀Javadoc,我們發現這兩個方法並不一定保證得到你所期望的精度。先來看System.currentTimeMillis():

Returns the current time in milliseconds. Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.

誠如上面所說返回值的粒度依賴於底層作業系統,那麼它在不同的平臺上到底能提供是麼樣的精度,是否像函式名所寫的那樣真正 確到1毫秒呢?看下面一段測試程式:

public class ClockAccuracyTest {

    public static void main(String args[]) {

        SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss:SSS");
        int size = 4000000;

        // create an array to hold millisecond times
        // and loop to capture them
        long times[] = new long[size];
        for (int i = 0; i < size; i++) {
            times[i] = System.currentTimeMillis();


        }

        // now display the unique times
        long time = times[0];
        long previousTime = times[0];
        long count = 0;
        Set<Long> deltas = new HashSet<Long>();       
        long delta = 0;
        long minDelta = Long.MAX_VALUE;
        long maxDelta = Long.MIN_VALUE;
        for (int i = 0; i < size; i++) {
            if (times[i] > time) {
                delta = time - previousTime;
                deltas.add(delta);
                if (delta > 0 && delta < minDelta) {
                    minDelta = delta;
                } else if (delta > maxDelta) {
                    maxDelta = delta;
                }

                System.out.print("raw=");
                System.out.print(time);
                System.out.print(" | formatted=");
                System.out.print(formatter.format(new Date(time)));
                System.out.print(" | frequency=");
                System.out.print(count);
                System.out.print(" | delta=");
                System.out.print(delta);
                System.out.println("ms");

                previousTime = time;
                time = times[i];
                count = 0;
            } else {
                count++;
            }
        }

        System.out.println("");
        System.out.println("Minimum delta : " + minDelta + "ms");
        System.out.println("Maximum delta : " + maxDelta + "ms");

    }

}

這段程式迴圈呼叫 System.currentTimeMillis()方法, 記錄並顯示結果,在我機器(Windows XP SP3)上執行輸出如下:

raw=1255659457187 | formatted=16-十月-2009 10:17:37:187 | frequency=147250 | delta=0ms
raw=1255659457203 | formatted=16-十月-2009 10:17:37:203 | frequency=447674 | delta=16ms
raw=1255659457218 | formatted=16-十月-2009 10:17:37:218 | frequency=436583 | delta=15ms
raw=1255659457234 | formatted=16-十月-2009 10:17:37:234 | frequency=439379 | delta=16ms
raw=1255659457250 | formatted=16-十月-2009 10:17:37:250 | frequency=426547 | delta=16ms
raw=1255659457265 | formatted=16-十月-2009 10:17:37:265 | frequency=447048 | delta=15ms
raw=1255659457281 | formatted=16-十月-2009 10:17:37:281 | frequency=459522 | delta=16ms
raw=1255659457296 | formatted=16-十月-2009 10:17:37:296 | frequency=414816 | delta=15ms
raw=1255659457312 | formatted=16-十月-2009 10:17:37:312 | frequency=458826 | delta=16ms

Minimum delta : 15ms
Maximum delta : 16ms

輸出的四列從左到右分別是原始的毫秒值、格式化的時間、每個值迴圈的次數、與上一個不同值的差。可以看到在Windows上 System.currentTimeMillis()方法並不能提供1ms的計時粒度,它的粒度為15~16ms,從網上的其它文章來看,這個結果基本上是一致的,這也驗證了Javadoc上所寫的“ the granularity of the value depends on the underlying operating system and may be larger ”。在其他作業系統,如Linux、Solaris上,我沒有進行測試,但從網上的一些測試結果看, currentTimeMillis方法在 某些作業系統能夠提供精確的1毫秒計時粒度。這是不是意味著Java在Windows上無法進行精確的毫秒計時了呢?當然不是,一種解決方案是採用JNI呼叫,利用Windows系統提供的函式計時;還有一個更簡便的辦法,就是使用JDK5.0加入的System.nanoTime()方法。Javadoc對該方法的描述如下:

Returns the current value of the most precise available system timer, in nanoseconds.

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.

它返回系統能夠提供的最為精確的計時,以納秒(10億分之一秒)為單位,但並不保證納秒級精度。把上面的測試程式中黑體的一行改為“times[i] = System.nanoTime();”,並把所有的“ms”該成“ns”,迴圈次數改為10。執行輸出如下:

raw=8705892679376 | formatted=17-十一月-2245 23:31:19:376 | frequency=1 | delta=0ns
raw=8705892681053 | formatted=17-十一月-2245 23:31:21:053 | frequency=0 | delta=1677ns
raw=8705892682449 | formatted=17-十一月-2245 23:31:22:449 | frequency=0 | delta=1396ns
raw=8705892683846 | formatted=17-十一月-2245 23:31:23:846 | frequency=0 | delta=1397ns
raw=8705892685522 | formatted=17-十一月-2245 23:31:25:522 | frequency=0 | delta=1676ns
raw=8705892686919 | formatted=17-十一月-2245 23:31:26:919 | frequency=0 | delta=1397ns
raw=8705892688316 | formatted=17-十一月-2245 23:31:28:316 | frequency=0 | delta=1397ns
raw=8705892689713 | formatted=17-十一月-2245 23:31:29:713 | frequency=0 | delta=1397ns
raw=8705892691110 | formatted=17-十一月-2245 23:31:31:110 | frequency=0 | delta=1397ns

Minimum delta : 1396ns
Maximum delta : 1676ns

我們看到frequency=0,這意味著每執行一次迴圈,nanoTime方法的返回值都發生了改變,改變的差值平均大約為1467ns(不同效能的機器結果會不同)。這說明nanoTime方法計時的精度是非常高的,粒度比方法本身的呼叫執行耗時還要小。

回到上面的問題,如何在windows上實現精確的毫秒計時呢。答案就是用“System.nanoTime()/1000000L”代替“System.currentTimeInMills()”。把測試程式黑體的一行程式碼改為"times[i] = System.nanoTime()/1000000L;",迴圈次數5000,執行輸出結果如下:

raw=9487129 | formatted=01-一月-1970 10:38:07:129 | frequency=202 | delta=0ms
raw=9487130 | formatted=01-一月-1970 10:38:07:130 | frequency=704 | delta=1ms
raw=9487131 | formatted=01-一月-1970 10:38:07:131 | frequency=621 | delta=1ms
raw=9487132 | formatted=01-一月-1970 10:38:07:132 | frequency=618 | delta=1ms
raw=9487133 | formatted=01-一月-1970 10:38:07:133 | frequency=696 | delta=1ms
raw=9487134 | formatted=01-一月-1970 10:38:07:134 | frequency=695 | delta=1ms
raw=9487135 | formatted=01-一月-1970 10:38:07:135 | frequency=698 | delta=1ms
raw=9487136 | formatted=01-一月-1970 10:38:07:136 | frequency=698 | delta=1ms

Minimum delta : 1ms
Maximum delta : 1ms

我們看到粒度delta變為了1ms。迴圈次數frequency平均為676,也就是說每個迴圈運執行耗時1/676=0.001479ms=1479ns,與上一個測試結果的1467ns吻合。

結論:如果你的Java程式需要高精度的計時,如1毫秒或者更小,使用System.nanoTime()方法,它完全可以滿足你的需求。如果不行的話,建議你換臺更快的機器試試:)