System.nanoTime()为什么不能作为数据库主键

1 System.nanoTime()定义

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

根据纳秒方法的注释:

Returns the current value of the running Java Virtual Machine’s high-resolution time source, 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 origin 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.

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

这样有点恐怖的是我们相同的代码在不同机器运行导致结果可能不同。所以它很少用来计算,通常都是测量先后顺序时间段,以前测试netty中的一个时间轮工具类(HasedTimeWheel)发现它不支持调系统时间, 比如一个任务是6点执行, 提交到时间轮后, 把系统时间调到7点,. 任务不会立即执行, 但是看HasedTimeWheel的代码感觉在设计上是支持调时间的(即调到7点后任务应该立即执行), 一顿测试后发现此类以System.nanoTime()作为当前时间, 要想支持调时间, 只需要把System.nanoTime()替换成System.currentTimeMillis()(相关时间单位也要改).
System.currentTimeMillis()返回的是自1970年到现在的毫秒数

2 比较时间段

System.nanoTime()的返回值要用相减是否大于0来判断调用的先后顺序, 但不能用><来判断.
System.nanoTime()返回的数值实际是64位无符号数, 随着进程运行时间增长, 溢出后再从0开始 , 赋值给long类型相当于当做补码数(有符号数)使用, 其值循环规律如下:

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

假设有两次先后顺序未知的调用, 且两次调用时间间隔小于2^63ns (约200+year)

	long t1 = System.nanoTime();
	long t2 = System.nanoTime();

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

综上所述,System.nanoTime()是不适合作为数据库主键

发布了334 篇原创文章 · 获赞 186 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/u012060033/article/details/103374912