Spring 工具类的 Stopwatch 计时的简单使用

HYF Lv3

我们在计算某个代码块或某个方法的具体执行时间/速度时,一般方法是获取执行前后的时间戳,并计算差值得到结果。但这种方式并不优雅,在同时计算多个代码块或一些方法的执行时间/速度时,由于可读性较差,可能引起错读而写出办法,写多了也比较烦人。本文将使用 Spring-Framework 的 StopWatch 类,优雅且便利的实现相同效果。

1 为什么要使用 StopWatch

接下来将使用以下两个方法作对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @author -侑枫
* @date 2023/8/4 18:52:01
*/
public class GetTime {
public static void main(String[] args) throws Exception {
useCurrentTimeMillis();
useStopWatchOne();
}

private static void useStopWatchOne() throws Exception {
StopWatch stopWatchOne = new StopWatch();
StopWatch stopWatchTwo = new StopWatch();
stopWatchOne.start("外层代码块");
stopWatchTwo.start("内层代码块a");
Thread.sleep(1000);
stopWatchTwo.stop();
System.out.println(stopWatchTwo.getLastTaskName() + "执行时间:" + stopWatchTwo.getLastTaskTimeMillis());
stopWatchTwo.start("内层代码块b");
Thread.sleep(2000);
stopWatchTwo.stop();
System.out.println(stopWatchTwo.getLastTaskName() + "执行时间:" + stopWatchTwo.getLastTaskTimeMillis());
Thread.sleep(4000);
stopWatchOne.stop();
System.out.println(stopWatchOne.getLastTaskName() + "执行时间:" + stopWatchOne.getTotalTimeMillis());
}

private static void useCurrentTimeMillis() throws Exception {
long startTimeOne = System.currentTimeMillis();
long startTimeTwo = System.currentTimeMillis();
Thread.sleep(1000);
long stopTimeTwo = System.currentTimeMillis();
System.out.println("内层代码块a执行时间:" + (stopTimeTwo - startTimeTwo));
Thread.sleep(2000);
long startTimeThree = System.currentTimeMillis();
Thread.sleep(3000);
long stopTimeThree = System.currentTimeMillis();
System.out.println("内层代码块b执行时间:" + (stopTimeThree - startTimeThree));
Thread.sleep(4000);
long stopTimeOne = System.currentTimeMillis();
System.out.println("外层代码块执行时间:" + (stopTimeOne - startTimeOne));
}
}

执行结果如下:

1
2
3
4
5
6
7
8
内层代码块a执行时间:1006
内层代码块b执行时间:3010
外层代码块执行时间:10037
内层代码块a执行时间:1000
内层代码块b执行时间:1999
外层代码块执行时间:7010

Process finished with exit code 0
两者对比,可以看出 StopWatch 的便利性和可读性。StopWatch 实现计算代码时间的流程如下:
1
2
3
4
先 new 一个 StopWatch 对象
再 start 开始计时
然后 stop 停止计时
最后通过 stopwatch.getLastTaskTimeMillis() 得出时间差
如果换成使用 System.currentTimeMillis() 呢?先得声明好几个 long 类型的局部变量,然后相互之间去减,烦人不说,稍微粗心一点(尤其是 CV 大法)时,很容易搞错,代码可读性也较差。

2 StopWatch 的其他用法

除了使用 stopwatch.getTotalTimeMillis() 方法获取局部(最近一次)的时间差之外,还可以使用 stopwatch.getTotalTimeSeconds() 来获取总的时间耗时,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void useStopWatchTwo() throws Exception {
StopWatch stopWatchOne = new StopWatch();
stopWatchOne.start("代码块a");
Thread.sleep(1000);
stopWatchOne.stop();
System.out.println(stopWatchOne.getLastTaskName() + "执行时间:" + stopWatchOne.getLastTaskTimeMillis());
stopWatchOne.start("代码块b");
Thread.sleep(2000);
stopWatchOne.stop();
System.out.println(stopWatchOne.getLastTaskName() + "执行时间:" + stopWatchOne.getLastTaskTimeMillis());
stopWatchOne.start("代码块c");
Thread.sleep(4000);
stopWatchOne.stop();
System.out.println(stopWatchOne.getLastTaskName() + "执行时间:" + stopWatchOne.getLastTaskTimeMillis());
System.out.println("代码总执行时间:" + stopWatchOne.getTotalTimeMillis());
}

执行结果:

1
2
3
4
5
6
代码块a执行时间:1012
代码块b执行时间:2011
代码块c执行时间:4009
代码总执行时间:7033

Process finished with exit code 0

除此之外,StopWatch 还提供了stopwatch.prettyPrint() 方法来更优雅的打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void useStopWatchThree() throws Exception {
StopWatch stopWatchOne = new StopWatch();
stopWatchOne.start("代码块a");
Thread.sleep(1000);
stopWatchOne.stop();
stopWatchOne.start("代码块b");
Thread.sleep(2000);
stopWatchOne.stop();
stopWatchOne.start("代码块c");
Thread.sleep(4000);
stopWatchOne.stop();
System.out.println(stopWatchOne.prettyPrint());
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
StopWatch '': running time = 7019842800 ns
---------------------------------------------
ns % Task name
---------------------------------------------
1004075800 014% 代码块a
2007161500 029% 代码块b
4008605500 057% 代码块c


Process finished with exit code 0

非常优雅且清晰的数据,不仅有耗时,还有占比,甚至还有任务名。其余的打印或用法还有很多,例如

1
2
3
4
5
6
7
getTotalTimeSeconds() 获取总耗时秒,同时也有获取毫秒的方法
prettyPrint() 优雅的格式打印结果,表格形式
shortSummary() 返回简短的总耗时描述
getTaskCount() 返回统计时间任务的数量
getTaskName() 返回最后一个任务对象的名称
taskCount() 累计执行的计时任务数量
还可以调用 suspend() 方法暂停计时、resume() 方法恢复计时、reset() 重新计时。
(如果你的项目中没有使用 Spring 全家桶,也可以使用 Hutool / Apache 的 StopWatch 来代替 System.currentTimeMillis())

最后附上本文所写源代码:StopWatch的简单使用

  • 标题: Spring 工具类的 Stopwatch 计时的简单使用
  • 作者: HYF
  • 创建于 : 2023-08-07 19:26:32
  • 更新于 : 2024-07-27 21:21:52
  • 链接: https://youfeng.ink/StopWatch-4ee89cb0d66c/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
Spring 工具类的 Stopwatch 计时的简单使用