我是陈皮,一个在互联网 Coding 的 ITer,个人微信公众号「陈皮的JavaLib」关注第一时间阅读最新文章。

文章目录

  • 前言
  • Spring StopWatch
  • 实践例子
  • 源码分析
  • lang3 StopWatch
  • 总结

前言

平常,我们想要统计某一段代码块,或某一个方法的执行时间,最简单的是采用如下的方式。

package com.chenpi;/*** @author 陈皮* @version 1.0* @description* @date 2022/4/2*/
public class ChenPi {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):1011

但如果我们想对多个代码段进行计时,以及每个阶段计时的占比等信息,如果还是采用如上的方式就会充斥比较多的与业务无关的代码,不够简洁。

Spring StopWatch

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

package com.chenpi;import org.springframework.util.StopWatch;/*** @author 陈皮* @version 1.0* @description* @date 2022/4/2*/
public class ChenPi {public static void main(String[] args) throws InterruptedException {// 声明一个计时器StopWatch stopWatch = new StopWatch("陈皮的计时器");// 开始计时stopWatch.start("发送MQ计时");Thread.sleep(1000);// 结束计时stopWatch.stop();// 打印统计System.out.println(stopWatch.prettyPrint());}}// 输出结果如下
StopWatch '陈皮的计时器': running time = 1005775500 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
1005775500  100%  发送MQ计时

Spring StopWatch 有以下几个常用方法:

  • StopWatch():构造一个计时器

  • StopWatch(String id):构造一个指定 id 的计时器

  • start():创建一个名为空字符串的计时任务,开始计时

  • start(String taskName):创建一个指定名称计时任务,开始计时

  • stop():结束当前任务的计时

  • getTotalTimeNanos():获取全部任务的执行时间,单位纳秒

  • getTotalTimeMillis():获取全部任务的执行时间,单位毫秒

  • shortSummary():获取简单的统计信息

  • prettyPrint():以友好方式输出总统计时间,以及各个阶段任务的执行时间

  • setKeepTaskList(boolean keepTaskList):是否在内部的列表中存储每一个任务

实践例子

当程序中有多个计时器时,可通过构造不同 id 的计时器来区分。以下演示多个不同计时器,统计不同阶段的执行时间。

package com.chenpi;import org.springframework.util.StopWatch;/*** @author 陈皮* @version 1.0* @description* @date 2022/4/2*/
public class ChenPi {public static void main(String[] args) throws InterruptedException {m1();m2();}private static void m1() throws InterruptedException {// 声明一个计时器StopWatch stopWatch = new StopWatch("m1计时器");stopWatch.start("查询数据库");Thread.sleep(1000);stopWatch.stop();stopWatch.start("逻辑计算");Thread.sleep(500);stopWatch.stop();System.out.println(stopWatch.prettyPrint());}private static void m2() throws InterruptedException {// 声明一个计时器StopWatch stopWatch = new StopWatch("m2计时器");stopWatch.start("远程调用");Thread.sleep(800);stopWatch.stop();stopWatch.start("发送MQ");Thread.sleep(200);stopWatch.stop();System.out.println(stopWatch.prettyPrint());}
}// 输出结果如下
StopWatch 'm1计时器': running time = 1516953200 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
1008091000  066%  查询数据库
508862200  034%  逻辑计算StopWatch 'm2计时器': running time = 1013080000 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
809345900  080%  远程调用
203734100  020%  发生MQ

源码分析

其实 StopWatch 底层实现很简单,对于每一个任务,在任务开始和结束时刻调用System.nanoTime()方法获取服务器当前的时间,然后计算每一个任务的执行时间,存储在内部。内部使用一个列表存储不同任务阶段的执行时间,最后打印输出。

package org.springframework.util;import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;import org.springframework.lang.Nullable;public class StopWatch {// 计时器idprivate final String id;// 是否将任务存储到任务列表中private boolean keepTaskList = true;// 存储全部任务的列表private final List<TaskInfo> taskList = new ArrayList<>(1);// 当前任务开始时间private long startTimeNanos;// 当前任务名称@Nullableprivate String currentTaskName;// 最后一个任务@Nullableprivate TaskInfo lastTaskInfo;// 总任务数private int taskCount;// 总的执行时间private long totalTimeNanos;// 构造一个id为空字符串的计时器public StopWatch() {this("");}// 构造一个指定id的计时器public StopWatch(String id) {this.id = id;}// 获取计时器idpublic String getId() {return this.id;}public void setKeepTaskList(boolean keepTaskList) {this.keepTaskList = keepTaskList;}// 开始计时public void start() throws IllegalStateException {start("");}// 开始一个指定任务名称的计时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();}// 停止任务计时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;}public boolean isRunning() {return (this.currentTaskName != null);}@Nullablepublic String currentTaskName() {return this.currentTaskName;}public long getLastTaskTimeNanos() throws IllegalStateException {if (this.lastTaskInfo == null) {throw new IllegalStateException("No tasks run: can't get last task interval");}return this.lastTaskInfo.getTimeNanos();}public long getLastTaskTimeMillis() throws IllegalStateException {if (this.lastTaskInfo == null) {throw new IllegalStateException("No tasks run: can't get last task interval");}return this.lastTaskInfo.getTimeMillis();}public String getLastTaskName() throws IllegalStateException {if (this.lastTaskInfo == null) {throw new IllegalStateException("No tasks run: can't get last task name");}return this.lastTaskInfo.getTaskName();}public TaskInfo getLastTaskInfo() throws IllegalStateException {if (this.lastTaskInfo == null) {throw new IllegalStateException("No tasks run: can't get last task info");}return this.lastTaskInfo;}public long getTotalTimeNanos() {return this.totalTimeNanos;}public long getTotalTimeMillis() {return nanosToMillis(this.totalTimeNanos);}public double getTotalTimeSeconds() {return nanosToSeconds(this.totalTimeNanos);}public int getTaskCount() {return this.taskCount;}public TaskInfo[] getTaskInfo() {if (!this.keepTaskList) {throw new UnsupportedOperationException("Task info is not being kept!");}return this.taskList.toArray(new TaskInfo[0]);}public String shortSummary() {return "StopWatch '" + getId() + "': running time = " + getTotalTimeNanos() + " ns";}public String prettyPrint() {StringBuilder sb = new StringBuilder(shortSummary());sb.append('\n');if (!this.keepTaskList) {sb.append("No task info kept");}else {sb.append("---------------------------------------------\n");sb.append("ns         %     Task name\n");sb.append("---------------------------------------------\n");NumberFormat nf = NumberFormat.getNumberInstance();nf.setMinimumIntegerDigits(9);nf.setGroupingUsed(false);NumberFormat pf = NumberFormat.getPercentInstance();pf.setMinimumIntegerDigits(3);pf.setGroupingUsed(false);for (TaskInfo task : getTaskInfo()) {sb.append(nf.format(task.getTimeNanos())).append("  ");sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append("  ");sb.append(task.getTaskName()).append('\n');}}return sb.toString();}@Overridepublic String toString() {StringBuilder sb = new StringBuilder(shortSummary());if (this.keepTaskList) {for (TaskInfo task : getTaskInfo()) {sb.append("; [").append(task.getTaskName()).append("] took ").append(task.getTimeNanos()).append(" ns");long percent = Math.round(100.0 * task.getTimeNanos() / getTotalTimeNanos());sb.append(" = ").append(percent).append('%');}}else {sb.append("; no task info kept");}return sb.toString();}private static long nanosToMillis(long duration) {return TimeUnit.NANOSECONDS.toMillis(duration);}private static double nanosToSeconds(long duration) {return duration / 1_000_000_000.0;}// 任务实体public static final class TaskInfo {// 任务名称private final String taskName;// 任务执行时间private final long timeNanos;TaskInfo(String taskName, long timeNanos) {this.taskName = taskName;this.timeNanos = timeNanos;}public String getTaskName() {return this.taskName;}public long getTimeNanos() {return this.timeNanos;}public long getTimeMillis() {return nanosToMillis(this.timeNanos);}public double getTimeSeconds() {return nanosToSeconds(this.timeNanos);}}}

StopWatch 使用起来简洁,支持多任务阶段统计,统计多任务时间占比等,统计结果直观。但是它也有不好的地方,就是一个 StopWatch 实例只能同时 start 一个 task,只能等这个 task 进行 stop 之后,才能继续 start 另一个 task。注意,StopWatch 实例不是线程安全的,也没必要进行同步处理。

lang3 StopWatch

Apache commons lang3 包下也有一个用于计时工具类 StopWatch。它还有暂停计时,恢复计时,设置分割点等功能。

org.apache.commons:commons-lang3:3.12.0

它主要有以下几个常用方法:

  • create():实例化一个计时器

  • createStarted():实例化一个计时器,并开始计时

  • StopWatch(final String message):实例化一个带有标识符的计时器

  • start():开始计时

  • split():设置分割点

  • getSplitTime():统计从 start 开始最后一个分割点的用时

  • reset():重置计时

  • suspend():暂停计时

  • resume():恢复计时

  • stop():停止计时

  • getTime():统计从 start 到当前时刻的同时

package com.chenpi;import org.apache.commons.lang3.time.StopWatch;/*** @author 陈皮* @version 1.0* @description* @date 2022/4/2*/
public class ChenPi {public static void main(String[] args) throws InterruptedException {// 声明一个计时器StopWatch stopWatch = new StopWatch("m1计时器");stopWatch.start();Thread.sleep(1000);System.out.println("start开始到现在的时间:" + stopWatch.getTime());stopWatch.split();Thread.sleep(500);System.out.println("start开始到最后一个split的时间:" + stopWatch.getSplitTime());stopWatch.split();Thread.sleep(500);System.out.println("start开始到最后一个split的时间:" + stopWatch.getSplitTime());// 重置计时stopWatch.reset();Thread.sleep(2000);stopWatch.start();Thread.sleep(1000);System.out.println("start开始到现在的时间:" + stopWatch.getTime());// 暂停计时stopWatch.suspend();Thread.sleep(3000);// 恢复计时stopWatch.resume();Thread.sleep(1000);// 结束计时stopWatch.stop();Thread.sleep(1000);System.out.println("start开始到stop结束的时间:" + stopWatch.getTime());System.out.println(stopWatch);}
}// 输出结果如下
start开始到现在的时间:1000
start开始到最后一个split的时间:1001
start开始到最后一个split的时间:1510
start开始到现在的时间:1004
start开始到stop结束的时间:2015
m1计时器 00:00:02.015

总结

  • 如果是简单的计算执行计时,可以使用 JDK 自带的类获取系统时间进行计时。
  • 如果需要多阶段计时,并且需要统计每个阶段的执行时间占比等信息,可以使用 StopWatch 工具类。
  • 推荐使用 Spring StopWatch,因为本身我们项目使用 Spring 框架比较多,这样就自带了 StopWatch。

本次分享到此结束啦~~

如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!

计时器工具 StopWatch相关推荐

  1. 【Unity3D 教程系列第 12 篇】如何用Unity写一个简易的计时器工具?

    这是[Unity3D 教程系列第 12 篇],如果觉得有用的话,欢迎关注专栏. 平常关于延时的处理很多人都喜欢用携程去处理,但是在 Unity 里,并不建议大家用过多的携程,因为会出现一些莫名其妙的 ...

  2. C# 计时工具 Stopwatch

    Stopwatch 1. Stopwatch类 2. 使用示例 1. Stopwatch类 添加命名空间:using System.Diagnostics; namespace System.Diag ...

  3. spring工具-StopWatch使用介绍

    spring包含了很多方便的实用的工具,今天就来学习StopWatch时钟⏰ StopWatch是Spring核心包中的一个工具类,它是一个简单的秒表工具,可以计时指定代码段的运行时间以及汇总这个运行 ...

  4. JStorm与Storm源码分析(八)--计时器工具-mk-timer

    Storm使用计时器线程来处理一些周期性调度事件. 与计时器相关的操作主要有:创建计时器线程.查看线程是否活跃.向线程中加入新的待调度事件.取消计时器线程 mk-timer方法用于创建一个计时器线程. ...

  5. 认识一个工具 Stopwatch

    Start(): 开始或继续测量某个时间间隔的运行时间. Stop(): 停止测量某个时间间隔的运行时间. ElapsedMilliseconds:获取当前实例测量得出的总运行时间(以毫秒为单位). ...

  6. 【Unity3D日常开发】Unity3D中实现计时器工具类-正计时、倒计时、暂停计时、加速计时

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有 ...

  7. Kotlin flow 实现计时器工具类

    class FlowCountDownTimer constructor(private var time: Int) {constructor(time: Int,callBack: (time: ...

  8. Unity 计时器工具类

    一个项目中可能要使用多个计时器,为了应对这种情况,写了一个计时器管理器 单例 public class Singleton<T> where T : new() {private stat ...

  9. FlexTime——Mac计时器工具小帮手!

    FlexTime mac版是Macos上一款多功能计时器,可以改善你的节奏,在您的计算机上安排定期休息.设置自定义日程安排,让您的Mac保持正常运行.甚至可以将常规音轨发送到iTunes,以便进行常规 ...

最新文章

  1. 独家 | 避免神经网络过拟合的5种技术(附链接)
  2. 魔法照片pascal程序
  3. laravel大型项目系列教程(六)之优化、单元测试以及部署
  4. 20 款优秀的数据可视化工具,总有一款你用的到!
  5. Controller的返回值
  6. Android官方开发文档Training系列课程中文版:高效显示位图之在非UI线程中处理图片
  7. 内存占用少,计算速度快!华为诺亚方舟Lab开源即插即用的多用卷积核(NeurIPS 2018)...
  8. python读取配置文件并添加字典中_Python如何使用ConfigParser读取配置文件
  9. 知识整理(你想要的Linux知识都在这里)
  10. Node.js 把抓取到的电影节目列表单发或者群发到QQ邮箱
  11. 微软删除最大的公开人脸识别数据集,只因员工离职?!
  12. (06)vtk修改默认鼠标操作,实现鼠标按键控制模型旋转
  13. 【JavaScript回顾】闭包
  14. 图书管理系统项目开发计划书
  15. redis列表list常用命令大全
  16. 财务系统服务器ebs系统,ebs系统(ebs财务系统是什么)
  17. mezzanine安装和使用
  18. excel 导入导出使用poi自定义注解
  19. [2017纪中10-25]嘟噜噜 约瑟夫问题 递推
  20. Python简单绘图一

热门文章

  1. 常用的性能测试工具介绍:LoadRunner和JMeter
  2. ROS使用教程--关于rosbag
  3. 想用代码改变世界?先用好Git和Github!
  4. 华为od机试真题 C++ 实现【篮球比赛】
  5. mysql外键查询_MySQL外键使用详解
  6. linux光盘有iscsi安装包,linux上安装iscsi软件
  7. secscan-authcheck(越权漏洞检测工具) 安装总结
  8. struts2 2.3 升级2.5.22配置(附带漏洞检测工具)
  9. 字节跳动2019春招第二次笔试编程题
  10. 软件服务外包大赛全员总结和心得