Java计时器工具StopWatch的具体使用

目录
  • 前言
  • 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 {

	// 计时器id
	private final String id;

    // 是否将任务存储到任务列表中
	private boolean keepTaskList = true;

    // 存储全部任务的列表
	private final List<TaskInfo> taskList = new ArrayList<>(1);

	// 当前任务开始时间
	private long startTimeNanos;

	// 当前任务名称
	@Nullable
	private String currentTaskName;

    // 最后一个任务
	@Nullable
	private TaskInfo lastTaskInfo;

    // 总任务数
	private int taskCount;

	// 总的执行时间
	private long totalTimeNanos;

    // 构造一个id为空字符串的计时器
	public StopWatch() {
		this("");
	}

    // 构造一个指定id的计时器
	public StopWatch(String id) {
		this.id = id;
	}

    // 获取计时器id
	public 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);
	}

	@Nullable
	public 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();
	}

	@Override
	public 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。它还有暂停计时,恢复计时,设置分割点等功能。

&nbsp;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。

到此这篇关于Java计时器工具StopWatch的具体使用的文章就介绍到这了,更多相关Java计时器工具StopWatch内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java Stopwatch类,性能与时间计时器案例详解

    在研究性能的时候,完全可以使用Stopwatch计时器计算一项技术的效率.但是有时想知道某想技术的性能的时候,又常常想不起可以运用Stopwatch这个东西,太可悲了. 属性: Elapsed 获取当前实例测量得出的总运行时间. ElapsedMilliseconds  获取当前实例测量得出的总运行时间(以毫秒为单位). ElapsedTicks  获取当前实例测量得出的总运行时间(用计时器计时周期表示). IsRunning   获取一个指示 Stopwatch 计时器是否在运行的值. 方法

  • Java计时器StopWatch实现方法代码实例

    下面提供三种计时器的写法供大家参考,大家可以自行选择自己钟爱的使用. 写法一(Spring 包提供的计时器): import java.text.NumberFormat; import java.util.LinkedList; import java.util.List; /** * Simple stop watch, allowing for timing of a number of tasks, * exposing total running time and running ti

  • Java计时器工具StopWatch的具体使用

    目录 前言 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) t

  • Java常用工具类库——Hutool的使用简介

    前言 Hutool 是一个小而全的 Java 工具类库,通过静态方法封装,降低相关 API 的学习成本,提高工作效率,使 Java 拥有函数式语言般的优雅,让 Java 语言也可以"甜甜的". Hutool 中的工具方法来自于每个用户的精雕细琢,它涵盖了 Java 开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当: Hutool 是项目中"util"包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专

  • JAVA宝藏工具hutool的使用

    目录 字符串.时间.金额转换 日期1 日期2 有限状态自动机-敏感词搜索 加解密 文件 雪花算法-ID生成 网络相关 URL相关 大家在系统研发过程中,总是会遇到需要自己自定义工具类的情况,做一些数据转换.字符串操作.日期处理.加解密.编解码.金额计算等等等等,每次做系统都会要把这些代码拷贝来拷贝去,费时费力还不优雅. 做java的应该有一部分知道这个工具,hutool,我可以称它为JAVA的宝藏工具了,一应俱全,一个pom引入依赖,再也不需要自己写工具类了,你的工程里甚至都不需要util这个p

  • [JAVA]十四种Java开发工具点评

    在计算机开发语言的历史中,从来没有哪种语言象Java那样受到如此众多厂商的支持,有如此多的开发工具,Java菜鸟们如初入大观园的刘姥姥,看花了眼,不知该何种选择.的确,这些工具各有所长,都没有绝对完美的,就算是老鸟也很难做出选择.在本文中我简要介绍了常见的十四种Java开发工具的特点,管中窥"器",希望能对大家有所帮助. 1.JDK (Java Development Kit) 2.Java Workshop 3.NetBeans 与Sun Java Studio 5 4.Borlan

  • Java 爬虫工具Jsoup详解

    Java 爬虫工具Jsoup详解 Jsoup是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址.HTML 文本内容.它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据. jsoup 的主要功能如下: 1. 从一个 URL,文件或字符串中解析 HTML: 2. 使用 DOM 或 CSS 选择器来查找.取出数据: 3. 可操作 HTML 元素.属性.文本: jsoup 是基于 MIT 协议发布的,可放心使用于商业项目. js

  • java Arrays工具类实例详解

    Arrays工具类属于java中常用的工具类 public static void sort(int[] a) public static void sort(int[] a,int fromIndex, int toIndex) public static void sort(long[] a) public static void sort(long[] a,int fromIndex, int toIndex) public static void sort(short[] a) publ

  • 秒表计时器以及STOPWATCH(实例讲解)

    Stopwatch:秒表计时器,用来记录程序的运行时间,通常用来测试代码在时间上的执行效率.(需要引用:System.Diagnostics.) Stopwatch sw=new Stopwatch();//实例化一个对象. sw.Start():开启计时器. sw.Stop():关闭计时器. sw.Elapsed:[属性]获取计时器开始到结束之间的时长. 下面的代码就可以测出某段执行代码的运行时间: using UnityEngine; using System.Diagnostics; pu

  • java分页工具类的使用方法

    说明:曾经在网上看过花样繁多的分页,很多都号称如何通用,但很多时候往往不尽如人意:有在分页类中还加入URL地址信息的,有在分页类中还进行分页动作处理(此动作完全属于操作数据库方面的事情)的.现在好了,经本人总结与提炼: 无论你是否自己手动分页,还是借助了框架进行分页.此工具类都可以帮助你达到稳定的分页效果(包括导航页码功能),而且使用方法也相对简单:好了,废话少说,代码如下: package test.dao; import java.util.List; /** * 用于分页的工具类 * @a

  • JAVA验证码工具实例代码

    工具类: package com.lhy.web.servlet; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.FileNotFoundException; import java.io.FileOutputStream; import

  • 详解Java常用工具类—泛型

    一.泛型概述 1.背景 在Java中增加泛型之前,泛型程序设计使用继承来实现的. 坏处: 需要进行强制类型转换 可向集合中添加任意类型的对象,存在风险 2.泛型的使用 List<String> list=new ArrayList<String>(); 3.多态与泛型 class Animal{} class Cat extends Animal{} List<Animal> list=new ArrayList<Cat>(); //这是不允许的,变量声明的

随机推荐