System.nanoTime()的返回值只和进程已运行的时间有关, 不受调系统时间影响.
以前测试netty中的一个时间轮工具类(HasedTimeWheel)发现它不支持调系统时间, 比如一个任务是6点执行, 提交到时间轮后, 把系统时间调到7点,. 任务不会立即执行, 但是看HasedTimeWheel的代码感觉在设计上是支持调时间的(即调到7点后任务应该立即执行), 一顿测试后发现此类以System.nanoTime()作为"当前时间", 要想支持调时间, 只需要把System.nanoTime()替换成System.currentTimeMillis()(相关时间单位也要改).
System.nanoTime()的返回值要用相减是否大于0来判断调用的先后顺序, 但不能用>或<来判断.
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, 此时用相减来判断仍然是正确的.
JDK里System.nanoTime()方法的注释
/**
* 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();