关键词: Java 毫秒 微秒 纳秒 System.currentTimeMillis() 误差
在对新写的超快xml解析器和xpath引擎进行效率测试时,为获取执行时间,开始也没多想就用了System.currentTimeMillis() 来做的。由此碰到一个极其诡异的问题,同样的代码循环执行数次,分析每一次的执行时间,发现一大部分执行时间为小于1毫秒,但其间也发现有相当一部分的执行时间有非常大的跳跃,而且时间都近似16毫秒左右。这个1毫秒和16毫秒结果,以计算机的运行速度看,差距是惊人的,必须找出其原因。
根据经验,在16毫秒时间内,cpu可以运算的指令数量是相当惊人的,所以可以基本断定这16ms的差距,应当不是cpu在执行指令,另外因为测试过程中gc输出也已经打开,未见gc发生,所以怀疑可能是发生了什么io阻塞,比如文件读写、加载类库、或者什么网络操作等,由于笔者测试的系统的环境比较复杂,其间有用到ehCache,数据库操作等,排查起来非常不容易。
在困扰了好一阵之后,忽然想到可能计时系统有误差,这才翻回来查了下System.currentTimeMillis() 的文档,原来这个方法调用了个native方法,获取的时间精度会依赖于操作系统的实现机制。奶奶的!
既然不准,就看看有没更准的方法,在jdk5源码中,挨着System.currentTimeMillis() 定义就是System.nanoTime() 方法,靠,一下来了个精准1000000倍的取纳秒的方法,不过看jdk文档介绍,这个方法的精度依然依赖操作系统,不过再不准也能达到微秒级的准确度,放心用吧! 结果测试结果一下准确了不少,没再发现比较大的跳跃了。
所以这里提醒做非常精确的时间统计的朋友,谨慎使用System.currentTimeMillis() 。
虽然用取纳秒的方法解决了我的问题,但对于为何使用System.currentTimeMillis() 会造成那么大的跳跃,一直无解,有点郁闷。幸运的是今天恰巧看到一个网友做的测试很有意思,用事实数据证明了这个跳跃的存在,分享给感兴趣的同学!
以下内容为转帖:
在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;
}