前言】在程序性能分析的过程中,最直观的方式就是通过统计程序的实际耗时来衡量代码的性能,从而了解程序的整体性能表现,以便快速定位出耗时长的位置进行分析优化。

以往我们统计的程序的运行时间,常常采用如下的方式:

public class StaticTimeTest {public static void main(String[] args) throws InterruptedException {long startTime = System.currentTimeMillis();Thread.sleep(1000);long endTime = System.currentTimeMillis();System.out.println("执行耗时(ms):"+ (endTime - startTime));}
}// 输出:执行耗时(ms):1012

如果想获取各阶段的耗时,以更友好的方式给出各个代码块的执行时间统计结果,往往需要封装更多的代码来完成。StopWatch给我们提供了更加优雅的方式,去统计程序各部分耗时情况,让我们更纯粹的关注于业务逻辑的本身,堪称耗时统计的利器。

StopWatch核心方法

在org.springframework.util包下我们可以找到这个工具类,作者在源码中标注的描述信息如下:

Simple stop watch, allowing for timing of a number of tasks, exposing total running time and running time for each named task.
Conceals use of System.nanoTime(), improving the readability of application code and reducing the likelihood of calculation errors.
Note that this object is not designed to be thread-safe and does not use synchronization.
This class is normally used to verify performance during proof-of-concept work and in development, rather than as part of production applications.
As of Spring Framework 5.2, running time is tracked and reported in nanoseconds.

Since: May 2, 2001
Author: Rod Johnson, Juergen Hoeller, Sam Brannen

从作者的这段描述信息中我们可以看到,StopWatch秒表工具允许对多个任务计时,显示程序总耗时和每个指定任务的运行时间。

其隐藏了System.nanoTime()的使用,通过该工具封装,提高应用程序代码的可读性,降低计算错误的可能性。

请注意,此对象不是为线程安全而设计的,并且不使用同步。通常用于概念验证工作和开发期间测试程序的性能,而不是作为生产应用程序的一部分。从Spring Framework 5.2开始,运行时间以纳秒为单位进行跟踪和报告。

Hutool基于Spring Framework的工具类,对秒表工具进行了进一步的封装,常用方法如下:

  • create(String id):创建一个指定id的计时任务
  • getId():获取StopWatch的ID,用于多个秒表对象的区分
  • setKeepTaskList(boolean keepTaskList): 设置是否在停止后保留任务
  • start():开始默认的新任务计时
  • start(String taskName):开始指定名称的新任务计时
  • stop():结束当前任务的计时
  • getTotalTimeNanos():获取所有任务的执行时间(纳秒)
  • getTotalTimeMillis():获取全部任务的执行时间(毫秒)
  • getTotalTimeSeconds():获取全部任务的执行时间(秒)
  • shortSummary():获取简单的统计信息
  • prettyPrint():以友好方式输出总统计时间,以及各个阶段任务的执行时间

StopWatch实战

Spring 框架工具类 StopWatch,可以用来对程序中代码块,或者方法进行计时,并且支持多阶段计时,以及阶段时间占比等统计,使用起来代码比较简洁、轻量。示例如下:

public class StaticTimeTest {public static void main(String[] args) throws InterruptedException {StopWatch stopWatch = new StopWatch("测试 task");stopWatch.start("任务阶段1");Thread.sleep(1000);stopWatch.stop();stopWatch.start("任务阶段2");Thread.sleep(2000);stopWatch.stop();stopWatch.start("任务阶段3");System.out.println("任务执行...");stopWatch.stop();System.out.println(stopWatch.prettyPrint());  }
}// 运行结果如下:任务执行...StopWatch '测试 task': running time = 3019289500 ns---------------------------------------------ns         %     Task name---------------------------------------------1008034300  033%  任务阶段12010897200  067%  任务阶段2000358000  000%  任务阶段3

源码分析

StopWatch的源码实现非常简单,可以看下springframework的源码如下:

 /*** Start a named task.* <p>The results are undefined if {@link #stop()} or timing methods are* called without invoking this method first.* @param taskName the name of the task to start* @see #start()* @see #stop()*/public void start(String taskName) throws IllegalStateException {if (this.currentTaskName != null) {throw new IllegalStateException("Can't start StopWatch: it's already running");}this.currentTaskName = taskName;this.startTimeNanos = System.nanoTime();}/*** Stop the current task.* <p>The results are undefined if timing methods are called without invoking* at least one pair of {@code start()} / {@code stop()} methods.* @see #start()* @see #start(String)*/public void stop() throws IllegalStateException {if (this.currentTaskName == null) {throw new IllegalStateException("Can't stop StopWatch: it's not running");}long lastTime = System.nanoTime() - this.startTimeNanos;this.totalTimeNanos += lastTime;this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);if (this.keepTaskList) {this.taskList.add(this.lastTaskInfo);}++this.taskCount;this.currentTaskName = null;}

看到这,大家可能会恍然大悟,StopWatch主要是围绕着System.nanoTime封了一层时间相减的外衣进行操作。
但妙就妙在,这层外衣足够的漂亮,足够的优雅。通过内部静态类TaskInfo 可以记录每个子任务的名称耗时信息,以及按格式化打印结果,尤其是针对多任务统计时更友好一点。

PS. System.nanoTime与System.currentTimeMillis的区别
System.nanoTime提供相对精确的计时,但是不能用他来计算当前日期。返回最准确的可用系统计时器的当前值,以纳秒为单位。此方法只能用于测量已过的时间,与系统或钟表时间的其他任何时间概念无关。
System.currentTimeMillis返回的是从1970.1.1 UTC 零点开始到现在的时间,精确到毫秒,平时我们可以根据System.currentTimeMillis来计算当前日期,星期几等,可以方便的与Date进行转换。

总结

StopWatch的封装很漂亮,日常可以使用秒表来进行耗时统计,性能评估,排查问题。
多读源码,向高手学习。Fighting…

耗时统计利器——StopWatch秒表相关推荐

  1. NDK/C++ 耗时统计类TimeUtils

    一.需求分析 在开发阶段中,经常会需要打印出某些方式或步骤的耗时情况,大致需求如下: 能打印出某个步骤的耗时: 有开关可以控制打开和关闭耗时统计: 使用方便. 二.准备工作 之前介绍过一个 log 输 ...

  2. 优雅的利用Mybatis插件实现sql查询耗时统计

    优雅的利用Mybatis插件实现sql查询耗时统计 一. Mybatis反射机制讲解 二. 代理模式讲解 静态代理 动态代理 JDK动态代理参考代码 Proxy.newProxyInstance(xx ...

  3. Android魔镜:方法耗时统计插件Mirror-基础篇

    晓锋,曾在PPTV工作,饿了么资深Android工程师,专注于Android单元测试.架构设计.性能优化.以及最新技术分享,个人博客:michaelzhong. 注:本篇是<Android魔镜: ...

  4. 神兵利器—Android方法耗时统计插件Mirror(上)

    1 前言 1.1 发生背景 有一天,Boss跑过来说,下次迭代我们要做蜂鸟团队App性能调优.对于一个大型成熟的App应用,在业务稳定后,往往会更加关注性能相关的表现.那么,Android App的性 ...

  5. android 方法统计,神兵利器—Android方法耗时统计插件Mirror(上)

    1 前言 1.1 发生背景 有一天,Boss跑过来说,下次迭代我们要做蜂鸟团队App性能调优.对于一个大型成熟的App应用,在业务稳定后,往往会更加关注性能相关的表现.那么,Android App的性 ...

  6. Golang 打印耗时统计、纳秒级耗时统计(统计函数执行时间)

    文章目录 Golang 打印耗时统计 普通demo 利用defer的简洁方法 利用defer更优雅的方法 纳秒级耗时统计 time.Now().UnixNano() Golang 打印耗时统计 参考U ...

  7. Qt QPushButton(二) 按下 ↓ 抬起↑ 耗时统计

    Qt QPushButton(二) 按下 ↓ 抬起↑ 耗时统计 按钮按下抬起 今天第一次使用按钮的按下抬起的槽函数 ,所以想尝试一下 这里的四个QPushButton 我在↑上面添加了 pressed ...

  8. C++ 程序耗时统计

    在编程过程中经常需要统计一个代码段的耗时,本文主要记录 C++ 常用的耗时统计方式. 系统存储时间常见的两种方式为: 存储从1970年到现在经过多少时间 使用结构体分别存储当前时间的年月日时分秒 主要 ...

  9. StopWatch(秒表)实现计时

    场景 统计某段代码的执行时间 System.currentTimeMillis()实现 还在用如下代码么,out了,丑陋不丑陋: long start = System.currentTimeMill ...

最新文章

  1. Cron 表达式极速参考
  2. Flutter开发之《头条 Flutter iOS 混合工程实践》笔记(54)
  3. Docker cgroups作用(十)
  4. 华为云平台使用手册_华为云首发全生命周期应用平台,四大能力解决政企上云五大难题...
  5. 汉字的动态编码与显示方案
  6. docker安装带管理界面的rabbitmq
  7. 【JavaWeb】IDEA中启动SpringBoot项目及解决相关报错问题
  8. SVPWM matlab建模
  9. 梦断A股:证监会发现了小米的违规之处
  10. 5G及移动边缘计算(MEC)学习笔记(1)
  11. [转]用python来开发webgame服务端(4)
  12. 从零开始修炼电脑维修秘籍
  13. HDU 4826 双向DP
  14. fnv1 java_笔记本热键驱动_笔记本万能fn键驱动 v1.1 通用版-126g驱动网
  15. matlab 死区,matlab中怎么产生死区
  16. Android系统设置选项的包名
  17. WINDWOS XP应用大全
  18. 苹果Mac电脑怎么删除文件
  19. 定义一个结构体student,存储学生的学号、名字、性别和年龄,读入每个学生的所有信息,保存在结构体中,并输出。
  20. 《圈外课程学习记录》1.3 将信息归类分组

热门文章

  1. java gridlayout 设置列宽,设置GridLayout列的宽度
  2. Android开发之GridLayout
  3. 欧拉定理 证明及推论
  4. 在gitcafe上部署hexo博客方法介绍
  5. 小强和小明猜生日(20min)
  6. 《唐伯虎点秋香》对白
  7. YOLT:You Only Look Twice: Rapid Multi-Scale Object Detection In Satellite Imagery 卫星图像快速多尺度目标检测
  8. 刚三岁的拼多多赴美上市!活跃用户数近3亿,仅次于淘宝京东……
  9. Python基础——数据类型—字符串
  10. Mybatis插入数据报错:java.sql.SQLException: Thread stack overrun