System.nanotime()

最后更新:2020-06-10

System.nanoTime()的返回值只和进程已运行的时间有关, 不受调系统时间影响.

该方法只能用来测量时段而和系统时间无关。它的返回值是从某个固定但随意的时间点开始的(可能是未来的某个时间)。不同的JVM使用的起点可能不同

/**
* Returns the current value of the running Java Virtual Machine's
* high-resolution time source, in nanoseconds.
*
* <p>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 <i>origin</i> time (perhaps in the future, so values
* may be negative).  The same origin is used by all invocations of
* this method in an instance of a Java virtual machine; other
* virtual machine instances are likely to use a different origin.
*
* <p>This method provides nanosecond precision, but not necessarily
* nanosecond resolution (that is, how frequently the value changes)
* - no guarantees are made except that the resolution is at least as
* good as that of {@link #currentTimeMillis()}.
*
* <p>Differences in successive calls that span greater than
* approximately 292 years (2<sup>63</sup> nanoseconds) will not
* correctly compute elapsed time due to numerical overflow.
*
* <p>The values returned by this method become meaningful only when
* the difference between two such values, obtained within the same
* instance of a Java virtual machine, is computed.
*
* <p> For example, to measure how long some code takes to execute:
*  <pre> {@code
* long startTime = System.nanoTime();
* // ... the code being measured ...
* long estimatedTime = System.nanoTime() - startTime;}</pre>
*
* <p>To compare two nanoTime values
*  <pre> {@code
* long t0 = System.nanoTime();
* ...
* long t1 = System.nanoTime();}</pre>
*
* one should use {@code t1 - t0 < 0}, not {@code t1 < t0},
* because of the possibility of numerical overflow.
*
* @return the current value of the running Java Virtual Machine's
*         high-resolution time source, in nanoseconds
* @since 1.5
*/
public static native long nanoTime();

其中有一段描述是:

*  <p>To compare two nanoTime values
*  <pre> {@code
* long t0 = System.nanoTime();
* ...
* long t1 = System.nanoTime();}</pre>
*
* one should use {@code t1 - t0 < 0}, not {@code t1 < t0},
* because of the possibility of numerical overflow.

JDK表明比较两个nanoTime的时候,应该用t1 - t2 > 0的方式来比较,而不能用 t1 > t2的方式来比较,因为nanoTime在获取时有数值溢出的可能。

The Nano time is not a ‘real’ time, it is just a counter that increments starting from some unspecified number when some unspecified event occurs (maybe the computer is booted up).

It will overflow, and become negative at some point. If your t0 is just before it overflows (i.e. very large positive), and your t1 is just after (very large negative number), then t1 < t0 (i.e. your conditions are wrong because t1 happened after t0)…..

But, if you say t1 - t0 < 0, well, the magic is that a for the same overflow (undeflow) reasons (very large negative subtract a very large positive will underflow), the result will be the number of nanoseconds that t1 was after t0….. and will be right.

In this case, two wrongs really do make a right!

System.nanoTime()返回的数值实际是64位无符号数, 随着进程运行时间增长, 溢出后再从0开始, 赋值给long类型相当于当做补码数(有符号数)使用, 其值循环规律如下:

最小负数 -> 0 -> 最大正数 -> 最小负数 -> …

假设有两次先后顺序未知的调用, 且两次调用时间间隔小于2^63ns (约200+year) long t1 = System.nanoTime(); long t2 = System.nanoTime();

如果t1和t2都是正数或t1和t2都是负数, 则t1和t2的差不会超过2^63, 它们相减不会溢出, t1>t2 <=> t1-t2>0, 且较大的数一定是后一次调用返回的, 所以用相减比较和><运算符比较都可以 ; 如果t1和t2一正一负, 设t2是正数, 对比它们作为无符号数时的值, 可以知道负数t1才是后一次调用返回的, 所以由t2>t1得出t2是后一次调用是错误的. 另外我们假设了两次调用间隔小于2^63, t2-t1的值一定大于2^63 (放在long里面是负数), 所以有t2-t1<0, 此时用相减来判断仍然是正确的.

Edgar

Edgar
一个略懂Java的小菜比