System.nanoTime()是Java中获取高精度、单调递增时间最可靠的方式,因其基于CPU TSC等硬件计时器,不受系统时钟调整影响,精度达纳秒级,适用于性能监控和微基准测试,但需成对调用、避免跨线程复用、注意JIT预热及GC干扰。

Java中用System.nanoTime()统计执行时长,是获取高精度、单调递增时间最可靠的方式,特别适合性能监控和微基准测试。
为什么选 nanoTime 而不是 currentTimeMillis
System.currentTimeMillis()受系统时钟调整影响(如NTP校时),可能回拨或跳变,导致计算出负值或异常大值;而nanoTime()基于单调递增的底层计时器(如CPU TSC),不受系统时间修改干扰,精度通常达纳秒级(实际取决于硬件和JVM实现),更适合测量耗时。
- 适用于方法执行、代码块、I/O操作等短时任务的精确耗时统计
- 返回值是相对起始点的纳秒数,不能直接表示“时刻”,只用于差值计算
- 两次调用差值可能为负——仅当JVM或OS存在严重bug时极罕见,正常逻辑下应做非负校验
正确使用 nanoTime 的基本模式
核心原则:成对调用,避免跨线程复用起始时间戳,不参与业务逻辑判断。
- 在待测代码前调用一次
long start = System.nanoTime(); - 在待测代码后立即调用
long end = System.nanoTime(); - 计算耗时:
long elapsed = end - start;(单位为纳秒) - 如需毫秒/微秒,再做换算:
elapsed / 1_000_000(ms)或elapsed / 1_000(μs)
常见陷阱与规避建议
看似简单,但实际易踩坑:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 不要在循环内反复调用 nanoTime 做累加——每次调用有开销,高频采样会放大误差;应在外层记录起止,内部只执行逻辑
- 避免跨线程共享 start 时间戳——不同线程调度不确定性会导致结果失真;每个线程独立计时
- 注意 JIT 预热影响——首次执行可能含编译开销,性能测试应先预热多次再采集稳定数据
- 日志或监控中慎用 toString 或字符串拼接输出纳秒值——可能触发GC,干扰真实耗时;优先用 long 运算+格式化输出
结合监控场景的实用封装示例
可封装为轻量工具类,支持自动计时与上下文记录:
public class Timer {
private final long start;
<pre class="brush:php;toolbar:false;">private Timer() {
this.start = System.nanoTime();
}
public static Timer start() {
return new Timer();
}
public long elapsedNanos() {
return System.nanoTime() - start;
}
public double elapsedMs() {
return elapsedNanos() / 1_000_000.0;
}}
使用方式简洁:Timer timer = Timer.start(); doWork(); log.info("cost: {}ms", timer.elapsedMs());


















