[Java/时间] 耗时计算与耗时观测
目录
- 需求描述
- Java应用程序的耗时计算库
- System.currentTimeMillis() : JDK内置方法
- System.nanoTime() : JDK内置方法
- Date#getTime() : JDK 内置方法
- StopWatch : Spring 框架
- StopWatch : Guava 框架
- StopWatch : common.lang3 版
- StopWatch : hutool 框架
- X 参考文献
需求描述
-
针对应用程序的性能优化工作,观察程序内部各处理阶段耗时,一直是一个基础操作。
-
在之前的工作中,通常会选择使用下面这种方法来统计耗时:
public static void main(String[] args) throws InterruptedException {
long startTime = System.currentTimeMillis();//获取毫秒级时间戳
Thread.sleep(1000);
long endTime = System.currentTimeMillis();
System.out.println("耗时:" + (endTime - startTime) + "ms");
}
- 弊端:
- 代码臃肿、繁琐。在需要统计耗时的点较多的时候,这种方式会显得有点繁琐,经过一些调研之后发现,可以使用StopWatch来处理统计代码中的耗时操作。
System.currentTimeMillis()
: 毫秒级时间戳System.currentTimeMillis()
: 纳秒级时间数(无法直接转时间戳、时间字符串,需要进行换算)
- 那么,应该是有相关的第三方开源库才对。
Java应用程序的耗时计算库
System.currentTimeMillis()
: JDK内置方法
参见: 需求描述章节
- currentTimeMillis/毫秒
public class TimeIntervalTest {
public static void main(String[] args) throws InterruptedException {
// 开始时间
long stime = System.currentTimeMillis();
// 执行时间(1s)
Thread.sleep(1000);
// 结束时间
long etime = System.currentTimeMillis();
// 计算执行时间
System.out.printf("执行时长:%d 毫秒.", (etime - stime));
}
}
System.nanoTime()
: JDK内置方法
- nanotTime/纳秒
public class TimeIntervalTest {
public static void main(String[] args) throws InterruptedException {
// 开始时间
long stime = System.nanoTime();
// 执行时间(1s)
Thread.sleep(1000);
// 结束时间
long etime = System.nanoTime();
// 计算执行时间
System.out.printf("执行时长:%d 纳秒.", (etime - stime));
}
}
Date#getTime()
: JDK 内置方法
import java.util.Date;
public class TimeIntervalTest {
public static void main(String[] args) throws InterruptedException {
// 开始时间
Date sdate = new Date();
// 执行时间(1s)
Thread.sleep(1000);
// 结束时间
Date edate = new Date();
// 统计执行时间(毫秒)
System.out.printf("执行时长:%d 毫秒." , (edate.getTime() - sdate.getTime()));
}
}
StopWatch : Spring 框架
import org.springframework.util.StopWatch;
StopWatch stopWatch = new StopWatch();
// 开始时间
stopWatch.start();
// 执行时间(1s)
Thread.sleep(1000);
// 结束时间
stopWatch.stop();
// 统计执行时间(秒)
System.out.printf("执行时长:%d 秒.%n", stopWatch.getTotalTimeSeconds()); // %n 为换行
// 统计执行时间(毫秒)
System.out.printf("执行时长:%d 毫秒.%n", stopWatch.getTotalTimeMillis());
// 统计执行时间(纳秒)
System.out.printf("执行时长:%d 纳秒.%n", stopWatch.getTotalTimeNanos());
以上程序的执行结果为:
执行时长:0.9996313 秒. 执行时长:999 毫秒. 执行时长:999631300 纳秒.
注:
Thread#sleep
方法的执行时间稍有偏差,在 1s 左右都是正常的。
StopWatch : Guava 框架
引入依赖
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
案例示范
import com.google.common.base.Stopwatch;
import java.util.concurrent.TimeUnit;
public class TimeIntervalTest {
public static void main(String[] args) throws InterruptedException {
// 创建并启动计时器
Stopwatch stopwatch = Stopwatch.createStarted();
// 执行时间(1s)
Thread.sleep(1000);
// 停止计时器
stopwatch.stop();
// 执行时间(单位:秒)
System.out.printf("执行时长:%d 秒. %n", stopwatch.elapsed().getSeconds()); // %n 为换行
// 执行时间(单位:毫秒)
System.out.printf("执行时长:%d 豪秒.", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
StopWatch : common.lang3 版
依赖
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
<scope>compile</scope>
</dependency>
核心API: org.apache.commons.lang3.time.StopWatch(秒表/计时器类)
案例示范
public static void main(String[] args) throws InterruptedException {
//创建1个秒表实例对象
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
// 执行时间(1s)
Thread.sleep(1000);
// 中间阶段,计1次
stopWatch.split();
System.out.println("step1-1 执行时长:" + stopWatch.getTime() + " 毫秒.");
System.out.println("step1-2 执行时长:" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
System.out.println("step1-3 执行时长:" + stopWatch.getTime(TimeUnit.MILLISECONDS) + " 毫秒.");
System.out.println("step1-4 执行时长:" + stopWatch.getTime(TimeUnit.NANOSECONDS) + " 纳秒.");
System.out.println("step1-5 执行时长:" + stopWatch.getSplitTime() + " 毫秒.");//瞬间计时,推荐
System.out.println("step1-6 执行时长:" + stopWatch.getSplitNanoTime() + " 纳秒.");//瞬间计时,推荐
// 结束计时
Thread.sleep(2000);
System.out.println("step2-1 执行时长:" + stopWatch.getTime() + " 毫秒.");
System.out.println("step2-2 执行时长:" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
System.out.println("step2-3 执行时长:" + stopWatch.getTime(TimeUnit.MILLISECONDS) + " 毫秒.");
System.out.println("step2-4 执行时长:" + stopWatch.getTime(TimeUnit.NANOSECONDS) + " 纳秒.");
System.out.println("step2-5 执行时长:" + stopWatch.getSplitTime() + " 毫秒.");//瞬间计时,推荐
System.out.println("step2-6 执行时长:" + stopWatch.getSplitNanoTime() + " 纳秒.");//瞬间计时,推荐
System.out.println("startTime :" + stopWatch.getStartTime() + " 时间戳(ms)");
}
- out
step1-1 执行时长:1007 毫秒.
step1-2 执行时长:1 秒.
step1-3 执行时长:1007 毫秒.
step1-4 执行时长:1007450900 纳秒.
step1-5 执行时长:1006 毫秒.
step1-6 执行时长:1006983200 纳秒.
step2-1 执行时长:3019 毫秒.
step2-2 执行时长:3 秒.
step2-3 执行时长:3019 毫秒.
step2-4 执行时长:3020120600 纳秒.
step2-5 执行时长:1006 毫秒.
step2-6 执行时长:1006983200 纳秒.
startTime :1733987201826 时间戳(ms)
- 注意事项
- 如果要使用这个工具的话,需要注意
split
后并不是计算的两个阶段之间的耗时,而是从start()
到当前阶段的耗时。- 如果想要计算两个阶段之间的耗时的话,可以写一个新的StopWatch来继承Apache的StopWatch,并拓展对应方法。
StopWatch : hutool 框架
- HuTool中的StopWatch使用方式与Apache中的大同小异,不过它实现了阶段之间的计数功能
引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.31</version>
</dependency>
案例示范
- 源码
import cn.hutool.core.date.StopWatch;
import java.util.concurrent.TimeUnit;
/**
* 耗时监控
*
* @author liushuang
* @date 2024/8/23 14:54
*/
public class TimeCost {
public static void main(String[] args) throws InterruptedException {
StopWatch stopWatch = new StopWatch();
stopWatch.start("第一阶段");
Thread.sleep(1000);
stopWatch.stop();
stopWatch.start("第二阶段");
Thread.sleep(2000);
stopWatch.stop();
stopWatch.start("第三阶段");
Thread.sleep(500);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
}
}
和
Apache Common Lang3
不同的是,这里的start
可以定义描述,stop
表示的是当前Task停止,所以在每个阶段都定义了一个start()
和一个stop()
。
最后通过prettyPrint
打印出监控信息,可以传入一个TimeUnit
,在结果中就会按这个时间单位打印,最终得到的结果如下:
- log
下面打印出了每个阶段的耗时,及其实践占用的百分比。
StopWatch '': running time = 3516 ms
---------------------------------------------
ms % Task name
---------------------------------------------
000001004 29% 第一阶段
000002004 57% 第二阶段
000000507 14% 第三阶段
有了这个结果之后,就可以定位到相对比较耗时的代码的位置,做出针对性的优化。
X 参考文献

本文作者:
千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen/p/18602627
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
本文链接: https://www.cnblogs.com/johnnyzen/p/18602627
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
2017-12-12 Linux之备份(tar)/解压与压缩(gzip,bzip2,xz)【待完善】
2017-12-12 [C++]Linux之进程间通信小结【待完善】