高并发下,使用System.currentTimeMillis()计算时间差出现负数

        最近在压测一个接口,使用如下方法获取时间差:

long start = System.currentTimeMillis();
object.methodinvoke();
long end = System.currentTimeMillis();
long cost= end - start;

打印的日志记录时间差,后来发现日志里出现了很多负数,感到很奇怪,查了资料,发现这个方法里面有坑!

来看看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.
     *
     * 

See the description of the class Date for * a discussion of slight discrepancies that may arise between * "computer time" and coordinated universal time (UTC). * * @return the difference, measured in milliseconds, between * the current time and midnight, January 1, 1970 UTC. * @see java.util.Date */ public static native long currentTimeMillis();

       这个方法返回的是当前系统时间的微秒数。所以,初步定位为获取系统时间出问题了。测试机使用的是windows系统,查看系统时间设置,发现有个Internet时间设置,会同步Internet时间服务器的时间。因此,当同步新的时间比之前的时间小时,就导致了时间差出现负数。这里先关闭网络时间同步,然后再压测,发现正常了!

       不过,实际开发中,使用System.currentTimeMillis()计算时间差,也并不靠谱。因为获取的值的颗粒度取决于底层的操作系统。例如,很多操作系统的时间颗粒度是10微妙,而且这个时间又可能受NTP影响而产生微调,当时间差小于此值时,时间差的计算会很不靠谱。

      高并发下System.currentTimeMillis()需慎用,比如当使用System.currentTimeMillis()给生成文件命名时,可能就会出现同名情况。

      正确的操作应该是这个样子的,使用System.nanoTime():

long start = System.nanoTime();
object.methodinvoke();
long end = System.nanoTime();
long cost= end - start;

      来看看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. * *

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()}. * *

Differences in successive calls that span greater than * approximately 292 years (263 nanoseconds) will not * correctly compute elapsed time due to numerical overflow. * *

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. * *

For example, to measure how long some code takes to execute: *

 {@code
     * long startTime = System.nanoTime();
     * // ... the code being measured ...
     * long estimatedTime = System.nanoTime() - startTime;}
* *

To compare two nanoTime values *

 {@code
     * long t0 = System.nanoTime();
     * ...
     * long t1 = System.nanoTime();}
* * 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();

      nanoTime()返回的是JVM运行的纳秒数,它只依赖于当前的jvm,并且不会出现同步的情况。所以在计算时间差时是准确的。

      另外,在计算当前日期的时候是要使用currentTimeMillis()的,因为它是相对于 midnight, January 1, 1970 UTC的时间长度,而nanoTime()是相对于jvm的运行时间,这个时间是不确定的。 

你可能感兴趣的:(编程解惑)