1. 概述

本文将探讨在Java中测量代码执行时间的多种方法。虽然这看起来简单,但实际操作中存在几个需要警惕的坑。

我们将从标准Java类库到第三方工具包,全面分析各种时间测量方案的优缺点和适用场景。

2. 基础测量方案

2.1. currentTimeMillis()

初次接触时间测量需求时,很多开发者会这样写:

long start = System.currentTimeMillis();
// 待测代码
long finish = System.currentTimeMillis();
long timeElapsed = finish - start;

这段代码逻辑清晰:获取开始时间戳 → 执行目标代码 → 获取结束时间戳 → 计算差值。但结果往往不准确,因为currentTimeMillis()测量的是墙上时钟时间(wall-clock time)。

⚠️ 墙上时钟时间容易受以下因素干扰:

  • 系统时间手动调整
  • 闰秒(leap second)影响
  • NTP时间同步服务校准

2.2. nanoTime()

java.lang.System类提供了更可靠的nanoTime()方法。根据JDK文档

"此方法仅用于测量经过时间,与任何系统时间或墙上时钟时间概念无关"

使用方式与前者类似:

long start = System.nanoTime();
// 待测代码
long finish = System.nanoTime();
long timeElapsed = finish - start;

✅ 关键优势:

  • 专门为时间测量设计
  • 不受系统时钟调整影响
  • 精度可达纳秒级

❌ 需要注意:

  • 返回值单位是纳秒,需手动转换(毫秒需除以1,000,000)
  • 虽然精度高,但实际更新频率(分辨率)可能低于纳秒级
  • 文档仅保证分辨率不低于currentTimeMillis()

3. Java 8 新方案

Java 8 引入的java.time包提供了更现代的时间处理方案,核心类包括InstantDuration。这些类都是不可变、线程安全的,并使用独立的Java时间刻度(Java Time-Scale)。

3.1. Java时间刻度

传统时间体系将一天分为:

  • 24小时 × 60分钟 × 60秒 = 86,400秒

但实际天文日长度存在微小差异。UTC时间刻度允许一天包含86,399或86,401秒(国际秒定义基于铯133原子辐射周期),以保持与太阳日的同步。

Java时间刻度则将每个日历日严格划分为86,400秒,不处理闰秒问题。

3.2. Instant

Instant表示时间轴上的瞬时点,本质是从1970-01-01T00:00:00Z开始的数字时间戳。通过Instant.now()获取当前时间戳:

Instant start = Instant.now();
// 待测代码        
Instant finish = Instant.now();
long timeElapsed = Duration.between(start, finish).toMillis();

✅ 优势:

  • 使用Duration.between()自动计算时间差
  • 支持灵活的时间单位转换(毫秒/秒/纳秒等)
  • 避免手动计算错误

4. StopWatch工具类

Apache Commons Lang提供的StopWatch是更专业的计时工具,适合复杂场景。

4.1. Maven依赖

pom.xml中添加:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

最新版本可在Maven仓库查询。

4.2. 使用StopWatch测量时间

基础用法:

StopWatch watch = new StopWatch();
watch.start();
// 待测代码
watch.stop();
System.out.println("Time Elapsed: " + watch.getTime()); // 输出: Time Elapsed: 2501

✅ 进阶特性:

  • suspend()/resume():暂停/恢复计时
  • split():记录分段耗时
  • 支持多任务计时(需调用reset()重置)

❌ 注意事项:

  • 非线程安全
  • 需要额外依赖

5. 总结

Java中测量时间的方法对比:

方法 精度 线程安全 适用场景
currentTimeMillis() 毫秒 简单场景(不推荐)
nanoTime() 纳秒 ✅ 推荐基础方案
Instant/Duration 纳秒 Java 8+项目
StopWatch 毫秒/纳秒 复杂计时需求

简单粗暴的结论:
日常开发直接用nanoTime()足够,代码简洁且准确。对于专业基准测试(benchmarking),建议使用JMH等框架,而非手动计时——这已超出本文讨论范围。


原始标题:Measure Elapsed Time in Java | Baeldung