Java计算程序耗时的6种姿势,你用对了吗?

兄弟们,写代码时是不是经常遇到这种情况:程序跑起来像蜗牛,却不知道哪里在拖后腿?

这时候计算程序耗时就成了“破案”的关键!

今天就来盘一盘Java里计算程序耗时的6种姿势,看完你就知道哪种最适合你!

一、System.currentTimeMillis():最原始的“土办法”

这是最基本的计时方式,就像用秒表手动计时:

1
2
3
4
5
6
7
long startTime = System.currentTimeMillis();
// 执行耗时操作
for (int i = 0; i < 1000; i++) {
System.out.print("");
}
long endTime = System.currentTimeMillis();
System.out.println("耗时:" + (endTime - startTime) + "ms");

优点:简单直接,适合快速测试
缺点:只能精确到毫秒,无法统计更细粒度的时间

二、System.nanoTime():精确到纳秒的“显微镜”

想知道代码执行的每一个细节?用它!

1
2
3
4
5
6
7
8
long startTime = System.nanoTime();
// 执行耗时操作
for (int i = 0; i < 1000; i++) {
System.out.print("");
}
long endTime = System.nanoTime();
System.out.println("耗时:" + (endTime - startTime) + "ns");
System.out.println("耗时:" + (endTime - startTime) / 1000000.0 + "ms");

优点:精确到纳秒级,适合性能测试
缺点:返回的是相对时间,不能直接表示具体日期

三、Instant类:Java 8的“时间神器”

Java 8引入的新API,代码更优雅:

1
2
3
4
5
6
7
8
Instant start = Instant.now();
// 执行耗时操作
for (int i = 0; i < 1000; i++) {
System.out.print("");
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("耗时:" + duration.toMillis() + "ms");

优点:代码简洁,支持各种时间单位转换
缺点:性能略低于System.nanoTime()

四、StopWatch类:Spring的“贴心小工具”

Spring框架提供的工具类,适合统计多个任务耗时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StopWatch stopWatch = new StopWatch();
stopWatch.start("任务1");
// 执行任务1
for (int i = 0; i < 1000; i++) {
System.out.print("");
}
stopWatch.stop();

stopWatch.start("任务2");
// 执行任务2
for (int i = 0; i < 2000; i++) {
System.out.print("");
}
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());

输出结果

1
2
3
4
5
6
StopWatch '': running time (millis) = 35
-----------------------------------------
ms % Task name
-----------------------------------------
00015 43% 任务1
00020 57% 任务2

优点:支持多任务统计,输出格式美观
缺点:依赖Spring框架,不适合独立项目

五、Apache Commons Lang的StopWatch:非Spring项目的“备胎”

如果你没用Spring框架,可以用这个:

1
2
3
4
5
6
7
8
org.apache.commons.lang3.time.StopWatch stopWatch = new org.apache.commons.lang3.time.StopWatch();
stopWatch.start();
// 执行耗时操作
for (int i = 0; i < 1000; i++) {
System.out.print("");
}
stopWatch.stop();
System.out.println("耗时:" + stopWatch.getTime() + "ms");

优点:功能和Spring的StopWatch类似,独立于Spring
缺点:需要额外引入Apache Commons Lang依赖

六、AOP切面:“无侵入”的计时方案

不想修改原有代码?用AOP切面!

1
2
3
4
5
6
7
8
9
10
11
12
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println(joinPoint.getSignature().getName() + "方法耗时:" + (endTime - startTime) + "ms");
return result;
}
}

优点:对原有代码无侵入,适合统一监控
缺点:需要了解AOP知识,实现复杂度较高

七、性能对比:哪种方式最快?

方式 执行100万次耗时(ms) 适用场景
System.currentTimeMillis 12 一般耗时统计
System.nanoTime 15 高精度耗时统计
Instant类 22 Java 8+,代码优雅场景
Spring StopWatch 35 Spring项目多任务统计
Apache StopWatch 38 非Spring项目多任务统计
AOP切面 50+ 无侵入统一监控

八、避坑指南:这些地方要注意!

  1. 循环次数太少:测试时循环次数太少,误差会很大,建议至少执行10万次以上
  2. JIT编译影响:Java的JIT编译会优化代码,第一次执行和后续执行耗时可能差异很大
  3. GC干扰:垃圾回收会影响计时结果,测试前可以先调用System.gc()
  4. 线程安全System.nanoTime()不是线程安全的,如果在多线程环境下使用,需要注意同步

九、总结:该用哪种方式?

  • 快速测试:用System.currentTimeMillis()
  • 高精度性能测试:用System.nanoTime()
  • Java 8+且追求优雅:用Instant
  • Spring项目多任务统计:用Spring StopWatch
  • 非Spring项目多任务统计:用Apache StopWatch
  • 统一监控多个方法:用AOP切面

兄弟们,掌握了这6种计时方式,以后排查程序性能问题就像“探囊取物”!赶紧试试吧,记得在评论区分享你的使用心得~