System.nanoTime()天生免疫时钟回拨,因其基于CPU高精度计数器或CLOCK_MONOTONIC,只进不退;适用于超时控制、限流、压测等时间间隔场景,但不可用于日志打点或跨线程延迟测量。

System.nanoTime() 本身不“规避”时钟回拨,而是天生免疫——它根本不受系统时钟调整影响。
原理:它不看墙钟,只数自己的脉搏
它不读取操作系统的时间(即 wall clock),而是直接读取 CPU 的高精度计数器(如 TSC)或内核提供的单调时钟(如 Linux 的 CLOCK_MONOTONIC)。这个计数器从 JVM 启动后开始持续累加,只进不退。哪怕运维凌晨三点把系统时间往回调 10 分钟,nanoTime() 的值依然平滑上涨,差值始终为正、始终真实。
- currentTimeMillis() 映射的是“现在几点”,会随 NTP 跳跃或手动改时而突变
- nanoTime() 回答的是“过了多久”,只依赖硬件计数器的物理流逝,与时间语义无关
适用场景:所有“经过多久”的判断都该换它
凡是逻辑依赖时间间隔而非具体时刻,就该用 nanoTime() 替代 currentTimeMillis():
- 网络/串口通信超时控制(如 500ms 等待响应)
- 抢购倒计时、限流窗口、滑动窗口统计
- 微秒级性能压测、算法 benchmark
- 环形缓冲区采样排序、延迟队列触发判定
正确用法:三个不能破的规矩
不是调两次减一减就安全了,踩错一条就可能失效:
立即学习“Java免费学习笔记(深入)”;
- 只做差值计算:返回值无物理意义,禁止存储、打印、跨线程传递、序列化
- 两次调用要足够紧邻:避免中间插入 GC、锁等待、线程挂起等长延迟操作;高频测量建议用 ThreadLocal 缓存起始值
- 单位换算用整除:转毫秒写
nanos / 1_000_000,别用Math.round(nanos / 1_000_000.0)引入浮点误差
注意事项:它不解决所有时间问题
nanoTime() 是单线程内可靠的单调时钟,但不保证跨核对齐,也不代表真实时间:
- 不能用于日志打点、数据库时间字段、定时任务调度——这些需要挂钟时间,仍得用 currentTimeMillis()
- 跨线程测量延迟时,不能直接拿 A 线程的 start 和 B 线程的 end 相减;需用 Phaser、CountDownLatch 等同步机制统一基准
- 多核环境若存在老旧虚拟机或 BIOS 关闭 TSC 同步,TSC 值可能有微秒级偏差;生产环境建议绑定线程到固定 CPU 核心


















