system.nanotime()适合测代码执行耗时,返回纳秒级单调时间,需两次调用相减;system.currenttimemillis()是墙钟时间,适合日志和超时,但受系统时钟调整影响;二者不可混用相减。

System.nanoTime() 适合测代码执行耗时
它返回的是纳秒级的“单调时间”,不受系统时钟调整影响,专门用来做性能测量。比如你想知道一段 for 循环跑了多少纳秒,或者两个算法谁更快,就该用它。
常见错误现象:System.nanoTime() 返回值是相对起点的差值,不是绝对时间;直接打印它毫无业务意义;有人拿它去算“当前时间”,结果发现和 new Date() 对不上——这很正常,因为它根本不是时间戳。
- 必须用两次调用相减:
long start = System.nanoTime(); doWork(); long elapsed = System.nanoTime() - start; - 不能跨 JVM 实例比较(不同进程的 nanoTime 起点不同)
- 虽然叫“纳秒”,但底层依赖硬件计时器,实际精度通常在微秒级(如 Linux 的
CLOCK_MONOTONIC),别指望真到 1ns
System.currentTimeMillis() 是“墙钟时间”,适合做日志、超时、定时
它返回的是自 1970-01-01 00:00:00 UTC 起的毫秒数,和系统时钟同步。你看到的日志时间、HTTP 请求的 Date 头、Timer 的延迟调度,都靠它。
容易踩的坑:currentTimeMillis() 会随系统时间跳变而突变。比如运维手动把服务器时间往前拨了 5 分钟,所有基于它的超时判断可能瞬间触发;NTP 校准也可能造成小幅度回跳,导致 elapsed = end - start 算出负数。
立即学习“Java免费学习笔记(深入)”;
- 不要用它测短耗时(毫秒级以下误差大,且受 GC 暂停干扰明显)
- 涉及分布式或跨机器的时间比对,要考虑时钟漂移,不能只信本地值
- Java 9+ 提供了
java.time.Instant.now(),底层其实也调currentTimeMillis(),语义更清晰,推荐新代码优先用
为什么不能混用 nanoTime 和 currentTimeMillis 做减法
它们起点不同、单位不同、更新机制不同。一个是从 JVM 启动(或某个硬件事件)开始的单调计数器,一个是实时同步的系统时钟毫秒值。强行相减得到的数字既不是真实时间,也不具备可解释性。
典型错误场景:有人想“校准” nanoTime,写 System.nanoTime() - System.currentTimeMillis(),以为能得出偏移量——这毫无意义,因为两者无数学关系。
-
System.nanoTime()的值可能远大于System.currentTimeMillis()(比如几十万亿 vs 十几亿) - 二者增长速率也不一致(nanoTime 是稳定递增,currentTimeMillis 可能暂停、跳跃)
- JVM 不保证两者之间存在固定换算关系,JIT 或 OS 层优化还可能让行为更不可预测
高并发下 nanoTime 的性能开销其实很小
很多人担心频繁调用 System.nanoTime() 影响吞吐,尤其在热点路径里。实际上现代 JVM(HotSpot)已将其内联为单条 CPU 指令(如 x86 的 RDTSCP),耗时通常在 20–50 纳秒,远低于一次 L1 cache miss。
但要注意:如果你在循环里每轮都调一次,又不做聚合,只是存着等最后算差值——那这些中间值基本没用,还占 GC 压力。真正要测的,是关键路径的“头尾差”,不是每个子步骤。
- 避免在每层方法入口/出口都打
nanoTime日志(用采样或 AOP 更合适) - 注意 JIT 优化可能导致空循环被消除,测出来是 0 —— 加个
Blackhole.consumeCPU()或写入 volatile 字段来防优化 - Android 上
nanoTime()精度较低(常为毫秒级),别默认它和桌面 JVM 表现一致
事情说清了就结束










