目录
1.1、前言
作为程序员在我们的日常编码过程中经常需要统计一段代码或者一个方法的执行时间,尤其是当以一个接口的执行响应时间比较长需要优化的时候,我们就需要统计接口实现方法中的那些代码片段执行比较耗时,然后再针对耗时较长的代码片段进行分析,优化代码的执行效率已达到优化接口的目的。
通常的做法是在代码片段开始的地方增加一句代码记录开始时间,再在代码结束的大方增加一句代码记录结束时间,结束时间减去开始时间得到代码的执行时间,然后输出即可。这样的做法虽然简单快速,但是其做法有着致命的缺陷,首先是在于对代码有侵入性,并且冗余代码多,我们需要不断的拷贝代码、修改代码、删除代码,尤其是在统计结束后或者需要将代码发布到正式生产环境的时,就需要将这些无效代码删除掉,特别的繁琐。总之就是这种方式不够优雅。本博文将介绍基于IDEA调试模式与StopWatch工具类如何优雅实现Java代码执行时间检测统计。
1.2、开发环境
本文将采用Java语言,基于JDK8基础环境、Maven、Idea等工具进行开发实现。JDK环境的安装请参考JDK安装部署。具体如下:
环境名称 | 版本号 |
---|---|
JDK | 1.8.0_202 |
Maven | 3.6.3 |
Idea | 2019.3.5 |
1.3、传统方式实现
有时候我们想要知道一段代码的执行时间,传统最简单快速的方式可以这样做,在代码片段开始的地方增加一句代码记录开始时间,再在代码结束的大方增加一句代码记录结束时间,结束时间减去开始时间得到代码的执行时间,然后输出即可。具体代码如下:
java
public void testOne() throws InterruptedException {
CodeExecuteTimeService service = new CodeExecuteTimeServiceImpl();
log.info("开始执行统计服务1。");
long start1 = System.currentTimeMillis();
service.func1();
long end1 = System.currentTimeMillis();
log.info("统计服务1执行结束。");
log.info("代码执行时间统计服务1耗时:{} 毫秒。",end1-start1);
log.info("开始执行统计服务2。");
long start2 = System.currentTimeMillis();
service.func2();
long end2 = System.currentTimeMillis();
log.info("统计服务2执行结束。");
log.info("代码执行时间统计服务2耗时:{} 毫秒。",end2-start2);
log.info("开始执行统计服务3。");
long start3 = System.currentTimeMillis();
service.func3();
long end3 = System.currentTimeMillis();
log.info("统计服务3执行结束。");
log.info("代码执行时间统计服务3耗时:{} 毫秒。",end3-start3);
}
代码执行结果如下:
从上面结果来看传统做法是没有任务问题的,代码的执行时间也能正确统计出来,但是这样的做法是不够优雅也不利于扩展,当后面要同时服务4、服务5甚至更多服务时间我们就需要重复的拷贝和修改代码,特别的繁琐。因此这样的做法不推荐。
1.4、优雅方式实现
优雅方式实现主要是基于IDEA调试模式与StopWatch工具类,充分利用了Spring提供的StopWatch工具的优势,实现了Java代码执行时间检测统计。
1.4.1、StopWatch工具简介
StopWatch
是 Spring 提供的一个工具类,用于测量代码段的执行时间,它可以帮助开发者了解代码的性能,以便进行优化。
StopWatch
主要提供以下功能:
开始和停止测量 :通过调用start()
方法开始测量代码段执行的时间,通过调用stop()
方法停止测量。
添加子任务 :可以调用start()
方法为当前任务添加一个子任务,方便对多个代码段进行时间跟踪。
获取详细时间报告 :调用toString()
方法可以获取当前StopWatch
的详细时间报告,包括所有子任务的开始时间、结束时间和总执行时间。
重置 :通过调用reset()
方法可以重置StopWatch
,恢复到初始状态,便于下次使用。StopWatch
工具的主要优点包括:
易于使用 :StopWatch
提供了简单的 API 来开始停止时钟,提供了方便的方式进行时间分析,不需要复杂的配置。
代码清晰 :使用StopWatch
可以清晰地区分多个代码块(时钟),增加了代码的可读性,易于理解那个部分的代码运行得慢。
简洁的时间输出 :通过使用StopWatch
,代码会自动执行,结果可以清晰地显示各个不同代码块完成所需的时间。
集成Spring框架 :由于是Spring提供的工具,与Spring框架的其他模块和注解(如@Async
)兼容,易于在Spring应用中集成,无缝支持并发任务和异步编程。
灵活性 :除了记录任务的执行时间外,根据需求还可以在Start
和Stop
事件间添加业务逻辑,以满足特定的性能监控需求。
1.4.2、实现步骤
编写StopWatch工具类,具体代码如下:
java
package cn.dz.smart.utils;
import org.springframework.util.StopWatch;
/**
* @ClassName StopWatchUtil.java
* @Author wzd
* @Version 1.0.0
* @Description 代码片段执行时间统计工具类
* @CreateTime 2024年06月30日 23:34:00
*/
public class StopWatchUtil {
private final static StopWatch STOP_WATCH = new StopWatch("代码运行时间检测");
/**
* @Description: 开始某个任务计时
* @Params: taskName 任务名称
* @Return: 无
* @Author: wzd
* @Date: 2024年6月30日23:36:39
*/
public static void start(String taskName) {
STOP_WATCH.start(taskName);
}
/**
* @Description: 停止某个任务计时
* @Params: 无
* @Return: 无
* @Author: wzd
* @Date: 2024年6月30日23:36:39
*/
public static void stop() {
STOP_WATCH.stop();
}
/**
* @Description: 打印所有任务的执行时间
* @Params: 无
* @Return: 无
* @Author: wzd
* @Date: 2024年6月30日23:36:39
*/
public static String print() {
return STOP_WATCH.prettyPrint();
}
}
在需要统计执行时间的代码片段的起始位置和结束位置打上断点,具体如下:
上面模拟了三耗时代码,在每段代码的起始位置和结束位置都有一条输出语句方便我们打断点,看这里的断点是黄色的,平常的断点是红色的,表示程序在调试模式下代码执行到此处时中断,但是这里是黄色的原因是我们对这个断点进行了修改,其目的是使其的作用从原来的中断程序变成动态插入一段代码,而这段代码则是我们检测代码执行时间所写的工具类。
修改断点,右键进入这个断点的修改配置页面,具体的修改项如下:
对断点有两处修改:
- 取消勾选Suspend,这个默认是勾选的,表示中断程序,也就是断点最基本的作用,因为我们的需求是打印执行时间,并不需要中断程序,所以这里不勾选。
- 在Evaluate and log里面填写我们要执行的代码,图上所示我们执行了一段代码:
java
StopWatchUtil.start("服务1")
- 最后点击【Done】按钮保存设置即可。
测试代码如下:
java
@Test
public void testTwo() throws InterruptedException {
CodeExecuteTimeService service = new CodeExecuteTimeServiceImpl();
log.info("开始执行统计服务1。");
service.func1();
log.info("统计服务1执行结束。");
log.info("开始执行统计服务2。");
service.func2();
log.info("统计服务2执行结束。");
log.info("开始执行统计服务3。");
service.func3();
log.info("统计服务3执行结束。");
log.info("所有统计服务执行结束,耗时统计如下:");
}
打完断点并修改完成后,通过调试模式执行代码(因为用了断点,所以要在调试模式下运行程序才能生效),得到的输出结果如下:
上述执行结果是STOP_WATCH.prettyPrint()方法所输出的内容,这部分是StopWatch类返回的,点进去可以看到源码,因为时间显示为纳秒,所以我们也可以自行定制成毫秒、秒,这点可以自行查阅StopWatch类的文档来实现。
现在来看这种实现方式是比较优雅的,该种实现方式对原来的代码没有侵入性,虽然我们写了一个工具类,但是只会在调试模式下、触发断点的地方使用到,当我们打包代码部署上线时,不需要对项目做任何的修改。因此该种方式是比较推荐的。
其完整的工程代码下载地址。