为什么用 System.nanoTime() / 1000000 而不是 System.currentTimeMillis()?

本文详细解析了Java中nanoTime()与currentTimeMillis()两种时间获取方法的区别,包括它们的用途、精度和相关性。并通过Zookeeper源码中的一处注释引发的思考,深入探讨了这两种方法在实际应用中的考量。

问题来源

以下是 zookeeper3.4.12 源码里的一小部分:

注释里有一句话是在读不懂:

Time.currentElapsedTime will change but nanoTime won't.

个人感觉应该是写错了(根据返回值,明显要变两个一起变),这里应该是说的 currentTimeMillis() 和 nanoTime() 的区别。

于是去 jdk 1.8 官方文档看了一下它们的区别(native 方法,看不见代码):

nanoTime()

以纳秒为单位返回正在运行的Java虚拟机的高分辨率时间源的当前值。 
该方法只能用于测量经过时间,与系统或挂钟时间的任何其他概念无关。 返回的值表示纳秒,因为一些固定但任意的起始时间(可能在将来,所以值可能是负数)。 在Java虚拟机的实例中,该方法的所有调用都使用相同的来源; 其他虚拟机实例可能会使用不同的来源。 

该方法提供纳秒精度(precision),但不一定是纳秒分辨率(resolution 即,值的变化频率) - 除了分辨率至少与currentTimeMillis() 的分辨率不同之外,不作任何保证 。 

跨越大约292年(2^{63} 纳秒)的连续呼叫的差异将无法正确计算由于数字溢出所造成的时间。 

仅当在Java虚拟机的同一实例中获得的两个此类值之间的差异被计算时,此方法返回的值才会变得有意义。 

例如,测量代码执行多长时间: 

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

比较两个nanoTime值 

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

应该使用t1 - t0 < 0 ,而不是t1 < t0 ,因为数值溢出的可能性。 

返回:运行Java虚拟机的高分辨率时间源的当前值,以纳秒为单位 

从以下版本开始: 

1.5 

currentTimeMillis()

返回当前时间(以毫秒为单位)。 请注意,虽然返回值的时间单位为毫秒,但该值的粒度(granularity)取决于底层操作系统,并且可能较大。 例如,许多操作系统以几十毫秒为单位测量时间。

有关“计算机时间”和协调世界时间(UTC)之间可能出现的轻微差异的讨论,请参阅类 Date 的说明。

返回:当前时间和1970年1月1日UTC之间的零点之间的差值,以毫秒为单位。

总结

区别:

1. 用途:
nanoTime() 不会单个出现,它用来精确地求时间差;
currentTimeMillis() 往往和 Date() 有关,例如 Date() 的源码:

public Date() {
        this(System.currentTimeMillis());
    }

2. 精度:
nanoTime() 纳秒;
currentTimeMillis() 取决于底层操作系统,有可能是几十毫秒

3. 相关性:

nanoTime() 与虚拟机的实例有关,不同虚拟机的实例之间比较无意义;
currentTimeMillis() 与系统设置的 UTC 时间有关,例如 win10 的 UTC 设置:

原问题分析

zookeeper 源码中的注释应该错了,应该是:

The difference is that if somebody changes the system clock,
currentTimeMillis will change but nanoTime won't.

感想和吐槽

看源码真的能暴露问题啊!

暴露源码的问题?

<think>好的,我现在需要解释Java中System.nanoTime()System.currentTimeMillis()的区别及适用场景。首先,我得回忆一下这两个方法的基本用途和特点。 System.currentTimeMillis()应该是返回当前时间的毫秒数,从1970年1月1日UTC开始的,也就是通常的时间戳。这个方法可能受到系统时间调整的影响,比如用户修改了系统时间,或者有闰秒之类的,所以它可能不适合用来测量时间间隔,特别是短时间的测量。 然后是System.nanoTime(),这个方法提供的是纳秒级别的计时,但它不是基于实际的时间戳,而是基于某个不确定的起点,比如JVM启动的时间。所以它更适合用来测量时间间隔,尤其是高精度的需求,比如性能测试或计算代码执行时间。不过,不同平台上的实现可能不同,跨平台的一致性可能有问题,这点需要注意。 接下来,我需要确认它们的主要区别。首先是精度,currentTimeMillis是毫秒,而nanoTime是纳秒,显然更高。然后是用途,currentTimeMillis适合获取当前时间戳,而nanoTime适合测量经过时间。另外,起点不同,currentTimeMillis是固定的UTC时间点,而nanoTime是随机的起点,可能在负数。还有是否受系统时间影响,currentTimeMillis会受系统时间调整的影响,而nanoTime不会,因为它只计算经过的时间。 适用场景的话,currentTimeMillis适合需要时间戳的情况,比如记录事件发生的时间,或者日期计算。而nanoTime适合需要高精度时间测量的情况,比如性能分析、短时间间隔的计算,比如计算一段代码的执行时间。 还需要注意跨平台的问题,因为nanoTime的实现可能依赖底层操作系统,不同JVM可能有不同的行为。例如,有的系统可能不支持纳秒级的精度,或者在不同操作系统上返回值的稳定性不同。这点在引用中有提到,比如引用3提到nanoTime是平台相关的,可能存在不同JVM实现的差异。 另外,关于时间戳的长度,currentTimeMillis返回的是13位(毫秒),而nanoTime返回的是纳秒数,可能更长,但通常只用来计算差值,而不是绝对时间。比如,引用4中提到,在需要非常短时间间隔的计算时应该使用nanoTime,而日期计算用currentTimeMillis。 可能还需要提到线程安全的问题,不过这两个方法应该都是线程安全的,因为它们只是读取系统状态,不涉及共享变量的修改。 现在需要组织这些点,结构要清晰。可能需要分点列出区别,然后适用场景。同时,引用相关的引用资料来支持观点,比如引用4中的内容。 还有用户的问题是想了解区别及适用场景,所以回答需要覆盖这两个方面。可能需要先介绍每个方法,然后对比它们的差异,最后给出各自的适用情况。同时,生成相关问题,比如如何选择这两个方法,或者它们在不同场景下的限制等。 最后,要确保使用正确的中文表达,避免技术术语错误,同时符合LaTeX格式要求,但这里的问题可能不需要数学公式,不过如果有变量或代码,需要正确格式化。例如,方法名可能需要用代码格式,比如`System.nanoTime()`。 总结一下,回答的结构应该是: 1. 两个方法的简介 2. 主要区别(精度、用途、起点、是否受系统时间影响、跨平台性) 3. 适用场景(各自适合的情况) 4. 注意事项(如nanoTime可能返回负数,currentTimeMillis的闰秒问题等) 5. 相关问题 需要检查引用是否正确应用,例如引用4提到选择方法的依据,引用3提到跨平台问题,引用2和1补充更多使用细节。</think>### Java中System.nanoTime()System.currentTimeMillis()的区别及适用场景 #### 1. 方法简介 - **`System.currentTimeMillis()`** 返回从**1970年1月1日UTC**至今的毫秒数(13位时间戳),常用于获取当前系统时间或日期计算[^4]。 - **`System.nanoTime()`** 返回基于**JVM启动或特定平台事件**的纳秒级计时值,适合测量时间间隔,但起点不确定且可能为负数[^3]。 #### 2. 核心区别 | **特性** | **System.currentTimeMillis()** | **System.nanoTime()** | |-----------------------|------------------------------------------|--------------------------------------| | **精度** | 毫秒级(1ms) | 纳秒级(1ns,实际精度依赖平台) | | **用途** | 获取时间戳、日期计算 | 测量代码执行时间、短时间间隔计算 | | **时间起点** | 固定(1970-01-01 UTC) | 不确定(通常为JVM启动时) | | **受系统时间影响** | 是(如系统时间修改会影响结果) | 否(仅测量经过时间) | | **跨平台一致性** | 是 | 否(不同JVM实现可能不同) | | **返回值范围** | 始终为正数 | 可能为负数(需用差值计算) | #### 3. 适用场景 - **优先使用`currentTimeMillis()`的情况** - 记录事件发生的时间戳(如日志时间)[^2]。 - 需要与日历日期转换的场景(如计算两天间隔)。 ```java long timestamp = System.currentTimeMillis(); // 输出:1659984000000(13位) ``` - **优先使用`nanoTime()`的情况** - 测量代码段执行时间(尤其是微秒/纳秒级需求)[^4]。 - 性能测试或高精度计时(如游戏循环、物理模拟)。 ```java long start = System.nanoTime(); // 执行代码 long duration = System.nanoTime() - start; // 单位:纳秒 ``` #### 4. 注意事项 - **`nanoTime()`的跨平台问题** 不同操作系统或JVM可能对纳秒精度的支持不一致,需测试验证。 - **`currentTimeMillis()`的闰秒问题** 系统时间可能因闰秒调整导致时间戳“回退”。 - **避免混合使用** 两者时间起点不同,差值计算无意义。 #### 5. 示例对比 | **场景** | **currentTimeMillis()** | **nanoTime()** | |------------------------|-------------------------|----------------------| | 获取当前时间 | ✔️ | ❌ | | 计算代码执行1ms内的耗时| ❌(精度不足) | ✔️ | | 跨时区的日期转换 | ✔️ | ❌ |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值