【填坑纪事】一次用System.nanoTime()填坑System.currentTimeMills()的实例记录
JDK提供了两个方法,System.currentTimeMillis()和System.nanoTime(),这两个方法都可以用来获取表征当前时间的数值。但是如果不仔细辨别这两个方法的差别和联系,在使用当中也很容易出错。笔者在前不久的工作当中使用System.currentTimeMillis()时就踩了一个大坑,后来在查明System.currentTimeMillis()和System.nanoTime()的特性后,才用System.nanoTime()来填了这个坑。本文,笔者就以自己的踩坑和填坑经历来介绍一下这两个方法。
一、事故回顾
文章的开头,笔者先描述一下自己前不久工作当中碰到的一个bug,分析过程以及解决办法。
1、问题描述
当手机按power键亮屏时,会调用人脸解锁功能来解锁手机。如果高频率不停地按power键,人脸解锁功能会被不停地调用,这样会产生很多并发问题,导致一些不可预料的异常,同时在这么高频率下,也没有必要每次亮屏都调用人脸解锁功能,需要过滤掉其中一部分调用。当时的处理办法是,当上一次调用人脸解锁功能的时候记录当前时间点 long mLastUnlockTime = System.currentTimeMillis(); 当再次调用的时候,也记录当前时间点 long nowUnlockTime = System.currentTimeMillis()。然后判断这两者的时间差 long durTime = nowUnlockTime - mLastUnlockTime,如果durTime<=300,表示距离上次调用不到300毫秒,本次不允许继续调用;如果durTime>300,表示距离上一次调用已经超过300毫秒了,则允许这一次继续调用,并把nowUnlockTime 的值赋给mLastUnlockTime,用于进行下一次的判断。
按照这个思路,就是防止300毫秒内连续调用两次人脸解锁功能,这种设计看起来似乎没什么问题。但是这个设计在正常运行了几个月后,测试人员提了一个bug:如果在系统设置中把时间往回调,那么人脸解锁功能就失效了。
2、原因分析
当收到这个bug后,我百思不得其解,调个系统时间能把人脸解锁功能给调失效了?我一度觉得这个现象很奇葩,不过作为一名老猿,我当然是去关键部分添加log分析原因了,最终定位到,是durTime出现问题了,居然出现了负数!!!这个时间差怎么会出现负数呢?仔细分析后才发现,这是System.currentTimeMills()的特性所致:该方法记录的是系统时间距离1970年1月1日的毫秒数。当把时间往前调了,本次获取的时间点nowUnlockTime 当然就小于上一次记录的时间值了,那自然而然 durTime 就是负数了。
3、解决办法
后来和某同事聊天,说起了这个听起来似乎挺奇葩的现象,同事说推荐我去了解一下System.namoTime()这个方法。后来用 System.namoTime() 取代 System.currentTimeMillis() 后,问题就迎刃而解了。
二、System.currentTimeMillis()
1、系统源码说明
咱们这里先看看系统类System.java中对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.
*
* <p> See the description of the class <code>Date</code> 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();
2、翻译
这里咱们翻译一下系统源码中的注释:
1 以毫秒的方式返回当前时间。请注意,虽然返回值的时间单位是毫秒,但是这个值的粒度取决于底层操作系统并且可能粒度更大。例如,许多操作系统是以几十毫秒为粒度测量时间的。
2 有关于“计算机时间”和协调世界时(UTC)之间的细微差别, 请查阅“Date”类。
3 @return 当前时间和1970年1月1日午夜之间的差值,以毫秒来测量。
4 @see java.util.Date
3、补充说明
(1)从源码中可以看到,这个方式是一个native方法,该值由底层提供。
(2)该方法可以用来计算当前日期,当前星期几等,与Date的换算非常方便,JDK提供了相关的接口来换算。
(3)通过该方法获取的值的依据是当前系统的日期和时间,可以在系统设置中进行设置和修改。
三、System.nanoTime()
1、系统源码说明
这里先看看系统类System.java中对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();
2、翻译
这里先翻译一下系统源码中的注释:
1 返回正在运行的Java虚拟机的高分辨率时间源的当前值,以纳秒计。
2
3 该方法可能仅仅用于测量已经逝去的时间,并且与任何其它系统或者挂钟时间概念无关。该返回值表示从某个固定但任意的原点时间(可能在未来,所以值可能是负数)开始的纳秒数。在一个java虚拟机实例中,所有该方法的调用都使用相同的原点;其它虚拟机实例很可能使用不同的源头。
4
5 该方法提供了纳秒级别的精度,但是不一定是纳秒级分辨率(也就是该值改变的频率)———— 除非这个分辨率至少和currentTimeMillis()一样好,否则将不会做任何保证。
6
7 在跨越大于292年(2的63次方纳秒)左右的连续调用中,这个差值将不能正确地计算已经过去的时间,因为数字溢出。
8
9 仅仅只有当在同一java虚拟机实例中获取的两个值之间的差值被计算时,返回值才有意义。
10
11 例如,去测量某代码执行花费了多长时间:
12 long startTime = System.nanoTime();
13 //...被测量的代码...
14 long estimatedTime = System.nanoTime() - startTime;
15
16 要比较两个nanoTime的值:
17 long t0 = System.nanoTime();
18 ...
19 long t1 = System.nanoTime()。
20 因为数字溢出的可能性,您应该使用"t1 - t0 < 0",而不是"t1 < t0"(来判断它们的大小,笔者注)。
21 @return 当前正在运行的java虚拟机的高精度时间资源值,以纳秒为单位。
22 @since 1.5
3、补充说明
(1)该方法也是一个本地方法,返回值由底层提供。
(2)如注释中所说,该方法从java 1.5开始引入。
(3)该方法所基于的时间是随机的,但在同一个JVM中,不同的地方使用的原点时间是一样的。
四、两者的区别与选择
前面对System.currentTimeMillis()和System.nanoTime()都分别从源码注释的角度进行了介绍,算是比较详细了,这里再简单终结一下,顺便谈一谈工作中如何选择:
(1)System.nanoTime()的精确度更高一些,如今的硬件设备性能越来越好,如果要更精密计算执行某行代码或者某块代码所消耗的时间,该方法会测量得更精确。开发者可以根据需要的精确度来选择用哪一个方法。
(2)单独获取System.nanoTime()没有什么意义,因为该值是随机的,无法表示当前的时间。如果要记录当前时间点,用System.currentTimeMills()。
(3)System.currentTimeMills()得到的值能够和Date类方便地转换,jdk提供了多个接口来实现;但是System.nanoTime()则不行。
(4) System.currentTimeMills()的值是基于系统时间的,可以人为地进行修改;而System.nanoTime()则不能,所以如文章开头笔者碰到的问题一样,如果需要根据时间差来过滤某些频繁的操作,用System.nanoTime()会比较合适。
五、结语
程序中往往某一行简单的代码,都包含了很多的知识点,真正推敲起来,似乎没有简单的东西。计算机世界浩如烟海,知识博大精深,我等只能望洋兴叹啊!!!本文的内容是基于自己平时的工作实践,然后做的总结,如果有错误的地方或者不妥的地方,还请不吝赐教,谢谢!
【填坑纪事】一次用System.nanoTime()填坑System.currentTimeMills()的实例记录的更多相关文章
- System.nanoTime与System.currentTimeMillis的理解与区别
System类代表系统,系统级的很多属性和控制方法都放置在该类的内部.该类位于java.lang包. 平时产生随机数时我们经常拿时间做种子,比如用System.currentTimeMillis的结果 ...
- System.nanoTime与System.currentTimeMillis的区别
平时产生随机数时我们经常拿时间做种子,比如用 System.currentTimeMillis的结果,但是在执行一些循环中使用了System.currentTimeMillis,那么每次的结 果将会差 ...
- System.nanoTime()的使用
纳秒 ns(nanosecond):纳秒, 时间单位.一秒的10亿分之一,即等于10的负9次方秒.常用作 内存读写速度的单位. 1纳秒=0.000001 毫秒 1纳秒=0.00000 0001秒 ja ...
- System.nanoTime
System.currentTimeMillis()返回的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数. System.nanoTime()返回的是纳秒,nanoTime而返回的可能是任意 ...
- System.nanoTime理解
JDK1.5之后java中的计时给出了更精确的方法:System.nanoTime(),输出的精度是纳秒级别,这个给一些性能测试提供了更准确的参考. 但是这个方法有个需要注意的地方,不能用来计算今天是 ...
- System.nanoTime与System.currentTimeMillis的区别(转)
原文地址:http://blog.csdn.net/dliyuedong/article/details/8806868 平时产生随机数时我们经常拿时间做种子,比如用System.currentTim ...
- 对于应用需要记录某个方法耗时的场景,必须使用clock_gettime传入CLOCK_MONOTONIC参数,该参数获得的是自系统开机起单调递增的纳秒级别精度时钟,相比gettimeofday精度提高不少,并且不受NTP等外部服务影响,能准确更准确来统计耗时(java中对应的是System.nanoTime),也就是说所有使用gettimeofday来统计耗时(java中是System.curre
对于应用需要记录某个方法耗时的场景,必须使用clock_gettime传入CLOCK_MONOTONIC参数,该参数获得的是自系统开机起单调递增的纳秒级别精度时钟,相比gettimeofday精度提高 ...
- 【算法】【网络流24题】巨坑待填(成功TJ,有时间再填)
------------------------------------------------------------------------------------ 17/24 --------- ...
- 我的Java开发学习之旅------>System.nanoTime与System.currentTimeMillis的区别
首先来看一道题:下面代码的输出结果是什么? import java.util.HashMap; import java.util.Map; public class HashMapTest { pub ...
随机推荐
- BuautifulSoup4库详解
1.BeautifulSoup4库简介 What is beautifulsoup ? 答:一个可以用来从HTML 和 XML中提取数据的网页解析库,支持多种解析器(代替正则的复杂用法) 2.安装 p ...
- Android Gradle 自定义Task 详解
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76408024 本文出自[赵彦军的博客] 一:Gradle 是什么 Gradle是一 ...
- 新导入的eclipse项目报错,找不到java包,找不到web.xml文件报错。
新导入的项目可能会出现报错,特别是web项目.我这里提供一种解决方法: 1.右击项目,选择“属性” 2.选择 Resource->java build path->libraries 图中 ...
- 微信小程序入门二
# 微信小程序开发实战 ## 准备 ### 课程概要 - 微信小程序基本介绍- 开发环境及工具的安装配置- 微信小程序的设计规范- 微信小程序基本结构分析- WXML和WXSS语法规范- 微信小程序A ...
- 重温《STL源码剖析》笔记 第一章
源码之前,了无秘密. --侯杰 经典的书,确实每看一遍都能重新收获一遍: 第一章:STL简介 STL的设计思维:对象的耦合性极低,复用性极高,符合开发封闭原则的程序库. STL的价值:1.带给我们一套 ...
- Java RandomAccessFile用法 【转】
RandomAccessFile源地址:http://blog.csdn.net/akon_vm/article/details/7429245 RandomAccessFile是用来访问那些保存数据 ...
- Avio红外热像仪在汽车行业的应用
红外热像仪利用红外探测器接受被测目标的红外辐射能量,将红外辐射能量转换成带有温度信息的图像信号,并通过显视屏等显示工具显示红外热图像.这种红外热图像与物体表面的温度分布相对应.红外热像仪能够将探测到的 ...
- Spring Aop技术原理分析
本篇文章从Aop xml元素的解析开始,分析了Aop在Spring中所使用到的技术.包括Aop各元素在容器中的表示方式.Aop自动代理的技术.代理对象的生成及Aop拦截链的调用等等.将这些技术串联起来 ...
- linux内核堆栈
一:进程的堆栈 内核在创建进程的时候,在创建task_struct的同时会为进程创建相应的堆栈.每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存 在于内核空间.当进程在用户空间运行时,c ...
- springboot中配置文件application.properties的理解
前言 Spring Boot使用"习惯优于配置"(项目中存在大量的配置,此外还内置了一个习惯性的配置,让你无需手动进行配置)的理念让你的项目快速运行起来.所以,我们要想把Sprin ...