java中获取比毫秒更为精确的时间

2023-11-15

关键词: 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() 会造成那么大的跳跃,一直无解,有点郁闷。幸运的是今天恰巧看到一个网友做的测试很有意思,用事实数据证明了这个跳跃的存在,分享给感兴趣的同学!

(原文链接:http://blog.csdn.net/elky1982/archive/2009/10/16/4677365.aspx

以下内容为转帖:

 

在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;
                }

                System.out.print("raw=");
                System.out.print(time);
                System.out.print(" | formatted=");
                System.out.print(formatter.format(new Date(time)));
                System.out.print(" | frequency=");
                System.out.print(count);
                System.out.print(" | delta=");
                System.out.print(delta);
                System.out.println("ms");

                previousTime = time;
                time = times[i];
                count = 0;
            } else {
                count++;
            }
        }

        System.out.println("");
        System.out.println("Minimum delta : " + minDelta + "ms");
        System.out.println("Maximum delta : " + maxDelta + "ms");

    }

}

 


这段程序循环调用 System.currentTimeMillis()方法, 记录并显示结果,在我机器(Windows XP SP3)上运行输出如下:

 

raw=1255659457187 | formatted=16-十月-2009 10:17:37:187 | frequency=147250 | delta=0ms
raw=1255659457203 | formatted=16-十月-2009 10:17:37:203 | frequency=447674 | delta=16ms
raw=1255659457218 | formatted=16-十月-2009 10:17:37:218 | frequency=436583 | delta=15ms
raw=1255659457234 | formatted=16-十月-2009 10:17:37:234 | frequency=439379 | delta=16ms
raw=1255659457250 | formatted=16-十月-2009 10:17:37:250 | frequency=426547 | delta=16ms
raw=1255659457265 | formatted=16-十月-2009 10:17:37:265 | frequency=447048 | delta=15ms
raw=1255659457281 | formatted=16-十月-2009 10:17:37:281 | frequency=459522 | delta=16ms
raw=1255659457296 | formatted=16-十月-2009 10:17:37:296 | frequency=414816 | delta=15ms
raw=1255659457312 | formatted=16-十月-2009 10:17:37:312 | frequency=458826 | delta=16ms

Minimum delta : 15ms
Maximum delta : 16ms

输出的四列从左到右分别是原始的毫秒值、格式化的时间、每个值循环的次数、与上一个不同值的差。可以看到在Windows上 System.currentTimeMillis()方法并不能提供1ms的计时粒度,它的粒度为15~16ms,从网上的其它文章来看,这个结果基本上是一致的,这也验证了Javadoc上所写的“ the granularity of the value depends on the underlying operating system and may be larger ”。在其他操作系统,如Linux、Solaris上,我没有进行测试,但从网上的一些测试结果看, currentTimeMillis方法在 某些操作系统能够提供精确的1毫秒计时粒度。这是不是意味着Java在Windows上无法进行精确的毫秒计时了呢?当然不是,一种解决方案是采用JNI调用,利用Windows系统提供的函数计时;还有一个更简便的办法,就是使用JDK5.0加入的System.nanoTime()方法。Javadoc对该方法的描述如下:

Returns the current value of the most precise available system timer, 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 time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.

它返回系统能够提供的最为精确的计时,以纳秒(10亿分之一秒)为单位,但并不保证纳秒级精度。把上面的测试程序中黑体的一行改为“times[i] = System.nanoTime();”,并把所有的“ms”该成“ns”,循环次数改为10。运行输出如下:

raw=8705892679376 | formatted=17-十一月-2245 23:31:19:376 | frequency=1 | delta=0ns
raw=8705892681053 | formatted=17-十一月-2245 23:31:21:053 | frequency=0 | delta=1677ns
raw=8705892682449 | formatted=17-十一月-2245 23:31:22:449 | frequency=0 | delta=1396ns
raw=8705892683846 | formatted=17-十一月-2245 23:31:23:846 | frequency=0 | delta=1397ns
raw=8705892685522 | formatted=17-十一月-2245 23:31:25:522 | frequency=0 | delta=1676ns
raw=8705892686919 | formatted=17-十一月-2245 23:31:26:919 | frequency=0 | delta=1397ns
raw=8705892688316 | formatted=17-十一月-2245 23:31:28:316 | frequency=0 | delta=1397ns
raw=8705892689713 | formatted=17-十一月-2245 23:31:29:713 | frequency=0 | delta=1397ns
raw=8705892691110 | formatted=17-十一月-2245 23:31:31:110 | frequency=0 | delta=1397ns

Minimum delta : 1396ns
Maximum delta : 1676ns

我们看到frequency=0,这意味着每执行一次循环,nanoTime方法的返回值都发生了改变,改变的差值平均大约为1467ns(不同性能的机器结果会不同)。这说明nanoTime方法计时的精度是非常高的,粒度比方法本身的调用执行耗时还要小。

回到上面的问题,如何在windows上实现精确的毫秒计时呢。答案就是用“System.nanoTime()/1000000L”代替“System.currentTimeInMills()”。把测试程序黑体的一行代码改为"times[i] = System.nanoTime()/1000000L;",循环次数5000,运行输出结果如下:

raw=9487129 | formatted=01-一月-1970 10:38:07:129 | frequency=202 | delta=0ms
raw=9487130 | formatted=01-一月-1970 10:38:07:130 | frequency=704 | delta=1ms
raw=9487131 | formatted=01-一月-1970 10:38:07:131 | frequency=621 | delta=1ms
raw=9487132 | formatted=01-一月-1970 10:38:07:132 | frequency=618 | delta=1ms
raw=9487133 | formatted=01-一月-1970 10:38:07:133 | frequency=696 | delta=1ms
raw=9487134 | formatted=01-一月-1970 10:38:07:134 | frequency=695 | delta=1ms
raw=9487135 | formatted=01-一月-1970 10:38:07:135 | frequency=698 | delta=1ms
raw=9487136 | formatted=01-一月-1970 10:38:07:136 | frequency=698 | delta=1ms

Minimum delta : 1ms
Maximum delta : 1ms

我们看到粒度delta变为了1ms。循环次数frequency平均为676,也就是说每个循环运运行耗时1/676=0.001479ms=1479ns,与上一个测试结果的1467ns吻合。

结论:如果你的Java程序需要高精度的计时,如1毫秒或者更小,使用System.nanoTime()方法,它完全可以满足你的需求。如果不行的话,建议你换台更快的机器试试:)

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java中获取比毫秒更为精确的时间 的相关文章

  • 仅将 Firesharp 用于 Windows 桌面推送通知

    我想在 Windows 桌面应用程序中使用 Firesharp 该应用程序只会接收来自 Firebase 的通知 并且不会有任何类型的数据库交互 Firebase Cloud Messaging FCM 是 Firebase 唯一使用的东西
  • 如何访问EmbeddedSolrServer实例的管理界面?

    在我的网络应用程序中 我正在运行org apache solr client solrj embedded EmbeddedSolrServer出于调试目的 我想访问管理界面 这就是我实例化服务器的方式 new EmbeddedSolrSe
  • 如何在android中使用retrofit访问404错误?

    我正在使用改造 2 访问 REST API 以使用原始正文插入 JSON 数据 我从服务器获得成功响应 但在响应时收到 404 错误 我想访问404错误请帮我解决这个问题 ApiUtil getServiceClass sendFinalC
  • 如何对 jar 文件资源使用 File.separator?

    我正在尝试读取位于 jar 文件中的属性文件 我想使用 File separator 因为应用程序将在多个平台上运行 我正在构建路径如下 jarFilePath jar file jarFile getAbsolutePath jarPro
  • 使用 SSL 和代理设置的 Rest 客户端获取连接超时

    我正在使用带有忽略 ssl 的 Rest 客户端 它工作正常 但在将来我尝试使用客户端证书进行的生产中将无法工作 我有 ca 证书和客户端证书 我用它创建了一个客户端 但我收到错误 Exception in thread main com
  • 批量设置命令的输出和错误以分隔变量

    在Windows 7批处理 cmd exe命令行 中 我试图将命令的标准输出 stdout 和标准错误 stderr 重定向到单独的变量 因此第一个变量设置为输出 第二个变量设置为输出 变量设置为错误 如果有 而不使用任何临时文件 我已经尝
  • 如何使用Python在没有窗口的情况下在屏幕上显示文本

    问题 我需要在没有窗口的情况下直接将文本写入屏幕 文本需要显示在所有其他窗口和全屏应用程序之上 并且不应以任何方式单击或交互 Example The text doesn t need to have a transparent backg
  • 对象映射器 - YAMLFactory - 由于缺少 _createContentReference 方法而出现异常

    我正在使用最新的 2 13 0 版本的 jackson 当我尝试解析 YAML 文件时 出现此异常 java lang NoSuchMethodError com fasterxml jackson core io ContentRefer
  • 检查更新时 Maven 无限期挂起

    我正在使用 Maven 构建一个项目 我是新手 并且它挂起 mvn package INFO Scanning for projects INFO INFO Building Presentation Reports INFO task s
  • 无法删除临时文件夹(有时)

    当我启动应用程序时 我创建一个临时文件夹 public static File createTempDir String name throws IOException File tempDir File createTempFile na
  • Java:如果数组大小未知,如何初始化?

    我要求用户输入 1 到 100 之间的一些数字并将它们分配到一个数组中 数组大小未初始化 因为它取决于用户输入数字的次数 我应该如何分配数组长度 如果用户输入 5 6 7 8 9 5 个数字 则 int list becomes int l
  • SwingUtilities.invokeLater

    我的问题与SwingUtilities invokeLater 我应该什么时候使用它 每次需要更新 GUI 组件时都必须使用吗 它到底有什么作用 是否有替代方案 因为它听起来不直观并且添加了看似不必要的代码 Do I have to use
  • Netty Nio java 中的通信

    我想在 Netty nio 中创建一个具有两个客户端和一个服务器的通信系统 更具体地说 首先 我希望当两个客户端与服务器连接时从服务器发送消息 然后能够在两个客户端之间交换数据 我正在使用本示例提供的代码 https github com
  • Java MYSQL/JDBC 查询从缓存的连接返回过时的数据

    我一直在 Stackoverflow 中寻找答案 但似乎找不到不涉及 Hibernate 或其他数据库包装器的答案 我直接通过 Tomcat 6 Java EE 应用程序中的 MYSQL 5 18 JDBC 驱动程序使用 JDBC 我正在缓
  • 如何根据从 jtextfield 和组合框接收的值将数据行添加到 Jtable

    我有一个JFrame表格有JTextFields JCombobox等等 我能够将这些值接收到变量 现在我想将接收到的数据添加到JTable当用户单击 添加 或类似的操作时在新行中 我创造了JTable使用 net beans 的问题是将这
  • 从侦听器中修改 JFrame [重复]

    这个问题在这里已经有答案了 可能的重复 如何在框架可见后调用 setUndecorated https stackoverflow com questions 875132 how to call setundecorated after
  • 已安装全局 NPM 包但未找到命令

    我已经全局安装了两个 npm 包 下载 https www npmjs com package download and 谜虚拟盒 https www npmjs com package enigmavirtualbox通过命令行 npm
  • AWS SQS Batch SendMessageBatchRequest 非常慢

    我的应用程序使用 SendMessageBatchRequest 将每个请求发布 10 条消息到 AWS SQS 每条消息的大小小于250字节 该应用程序预计每天发布约一百万条记录 但要实现这一目标 消息发布的速度非常慢 AmazonSQS
  • Zookeeper 未启动,nohup 错误

    我已经下载了zookeeper 3 4 5 tar gz 解压后我将conf zoo cfg写为 tickTime 2000 dataDir var zookeeper clientPort 2181 现在我尝试通过 bin zkServe
  • 在 Spark MLlib 上使用 Java 中的 Breeze

    在尝试从Java使用MLlib时 使用微风矩阵运算的正确方法是什么 例如scala 中的乘法很简单 matrix vector 相应的功能在Java中是如何表达的 有一些方法 例如 colon times 可以通过正确的方式调用 breez

随机推荐

  • STL之list(二)

    默认allocator为alloc 其具体使用版本请参照
  • HTML画布与SVG(Canvas vs. SVG)

    目录 画布 Canvas 什么是 Canvas 创建 Canvas 元素 通过 JavaScript 来绘制 理解坐标 更多 Canvas 实例 实例 线条 实例 圆形 实例 渐变 实例 图像 相关页面 SVG Scalable Vecto
  • 信号量的基本概念总结

    以下内容是摘抄博客 https blog csdn net fern girl article details 61197995 信号量的基本概念 一 首先 我们要知道信号量是什么 信号量的本质是数据操作锁 它本身不具有数据交换的功能 而是
  • FileSystemObject组件新建/读取/添加/修改/删除功能实例

  • MAC快捷键对照表

    Mac 键盘符号说明 Command Shift Caps Lock Option Control Return Enter Delete 向前删除键 Fn Delete 上箭头 下箭头 左箭头 右箭头 Page Up Fn Page Do
  • matlab kl散度,基于KL散度的面向对象遥感变化检测

    3 2 实验过程与结果 3 2 1 面向对象的遥感影像分割 利用ENVI软件的Segment Only Feature Extraction功能模块对实验数据进行面向对象分割操作 该方法采用的是Full Lambda Schedule分割算
  • 多益2018春招前端技术面试

    2018春招 多益网络内推 前端开发工程师 技术面问题 据我了解 从一月到三月份年 多益一共启动了至少三批春招招聘 从同学的反馈来看 一月份考验刚结束的那批春招通过率比较高 至于我个人 参加的是三月份上中旬的春招内推 可惜并没有通过 总体上
  • 虚拟机克隆后无法连接网络

    本文章转载自 Linux 无线图标莫名消失的解决方法 xin1889的博客 CSDN博客 今天我的也是的觉得再弄个虚拟机麻烦 索性就直接克隆了 然后连不上网 就连接原来的可以 连接百度也可以 但是唯独ping自己不行 然后不知道怎么回事 同
  • 智能指针和函数模板

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
  • c++学习笔记-#pragma once

    pragma 预处理宏的一般格式 pragma one one是参数 1 pragma message message 当编译器遇到这条指令时 它会在编译输出窗口中将message打印出来 常用于宏条件条件判断中使用 这样就知道自己的宏条件
  • 串口通信知识点总结

    串口通信小试牛刀 一 串口协议和RS 232标准 一 串口通讯协议 1 简介 2 串口通信的基本原理 二 RS232串口标准 1 简介 2 RS232串行接口的特点 3 RS232串行接口的电气特性 二 RS232电平与TTL电平 一 TT
  • Pytorch中多GPU并行计算教程

    如果不想看文字的 可以在我bilibili上看录制的视频教程 Pytorch多GPU使用教程 本教程所涉及的代码可自行在我的github上下载 https github com WZMIAOMIAO deep learning for im
  • layui添加菜单和动态操作tab

    layui添加菜单和动态操作tab 代码一 使用模板引擎渲染菜单 代码二修改版 代码一 使用模板引擎渲染菜单
  • JQ奇偶选择

    table tr even click function console log 选择奇数行 表示获取一个table 所有的索引为偶数的行 其中索引index 从0开始算起 0算偶数 table tr odd click function
  • Java运行时一个类是什么时候被加载的?

    A question a day get offer every day 未来的魔法师 一个类在什么时候开始被加载 Java虚拟机规范 中并没有进行强制约束 交给了虚拟机自己去自由实现 HotSpot虚拟机是按需加载 在需要用到该类的时候加
  • ThreadLocal源码分析,线程局部变量,内存泄漏?

    ThreadLocal作为线程局部变量 线程级的 单个线程内共享的 一般来说可以有两方面的用途 作为共享变量 在某些方法计算的结果 要共享到其他方法 在使用时 通过threadLocal set 设置值 通过threadLocal get
  • 源码断点分析Spring的占位符(Placeholder)是怎么工作的

    项目中经常需要使用到占位符来满足多环境不同配置信息的需求 比如
  • 国内可用的ntp服务器地址

    ntp sjtu edu cn 202 120 2 101 上海交通大学网络中心NTP服务器地址 s1a time edu cn 北京邮电大学 s1b time edu cn 清华大学 s1c time edu cn 北京大学 s1d ti
  • 程序员常用的计算机cmd指令

    windows cmd 查看command命令帮助说明 calc 计算器 mspaint 图画 notepad 记事本 dir 遍历当前目录 cd 路径名 进入该目录 cd 返回上级目录 netstat ano 查看端口占用 netstat
  • java中获取比毫秒更为精确的时间

    关键词 java 毫秒 微秒 纳秒 System currentTimeMillis 误差 在对新写的超快xml解析器和xpath引擎进行效率测试时 为获取执行时间 开始也没多想就用了System currentTimeMillis 来做的