频繁调用 system.currenttimemillis() 因 jni 和内核态切换导致性能损耗,可用 volatile 缓存时间戳(如每 1–5ms 更新)或批量获取优化;测耗时用 nanotime,建模用 instant,高精度场景慎用定时更新。

频繁调用 System.currentTimeMillis() 在高并发或高频场景下确实会带来明显性能损耗,核心原因是每次调用都触发一次 JNI 调用,进而引发用户态到内核态的切换(如 Linux 下调用 gettimeofday()),在多线程争抢时还会因共享时钟源产生竞争等待。
用缓存时间戳替代实时调用
适用于对毫秒级精度容忍小幅偏差(如日志打点、监控采样、防抖限频)的场景。关键不是“完全不准”,而是“够用且省”。
- 启动一个低优先级后台线程,每 1–10 毫秒更新一次全局 volatile 变量,例如:
private static volatile long cachedTime = System.currentTimeMillis(); - 业务代码直接读取该变量,避免任何 native 调用;注意无需加锁,volatile 已保证可见性
- 更新间隔不宜过长(否则误差放大),也不宜过短(徒增线程调度开销),常见取值为 1ms 或 5ms
按需批量获取,减少调用密度
把原本分散在各处的单次调用,收敛到逻辑入口或批处理节点统一获取。
- 例如在 Web 请求拦截器中一次性获取时间戳,注入到上下文(如
RequestContext),后续业务模块复用该值 - 流式处理(如 Kafka 消费)中,在每批次拉取后记录一次起始时间,整批事件共用该基准时间
- 避免在循环体内部、对象构造器、getter 方法中隐式调用——这些位置极易被高频触发
区分用途,选对时间 API
不是所有“要时间”的地方都该用 currentTimeMillis()。
- 测耗时?用
System.nanoTime():无系统调用开销,不受时钟调整影响,精度更高 - 建模时间点?用
Instant.now().toEpochMilli():语义清晰、线程安全,底层虽也调用 native,但 JDK 8+ 有优化,且更利于类型化设计 - 仅需相对顺序?考虑用单调递增序列号(如 AtomicLong 自增)代替时间戳,彻底规避系统时钟依赖
谨慎使用定时更新方案
网上流行的“单线程定时刷新 AtomicLong”模式有一定效果,但存在隐含成本:
-
ScheduledExecutorService本身引入调度器开销和线程管理负担 - 若精度要求高(如 1ms),固定周期可能因 GC 或调度延迟导致更新不稳
- 更轻量的做法是纯 while + sleep:用
volatile long+ 后台线程休眠更新,不依赖调度框架











