StopWatch—— 优雅的程序计时器

2023-04-25 18:55:56

StopWatch—— 优雅的程序计时器

程序计时器

在我们的开发过程中,为了更好的监测系统,接口,业务的运行情况,我们一般都会监测程序运行的时间,最初的时候我们是使用System.currentTimeMillis()获得开始和结束时间再进行相减从而获得程序运行时间,但是这种方式很繁琐也不够简洁,清晰。

StopWatch

介绍

StopWatch在Spring,Apache,hutool的工具包中都有,里边的实现原理都差不多,只不过有一些API的调用和使用不太一样。

我们先来看一下StopWatch的使用:

相信看过SpringBoot源码的同学对这个应该并不陌生,在SpringBoot程序启动的时候,就用到了StopWatch:

public ConfigurableApplicationContext run(String... args) {
		 stopWatch = new StopWatch();// 声明一个StopWatch实例
		stopWatch.start();//任务启动
        ……
        stopWatch.stop();// 任务停止
		return context;
	}

看SpringBoot的例子可以知道,StopWatch的使用基本分为三步:创建实例,启动,停止。但是作为spring的工具类,StopWatch会这么简单吗?当然不会。

我们来看一下具体的使用:

public static void main(String[] args) {
        // 构造方法的参数表示的是StopWatch实例的id,标志着实例的身份
        StopWatch watch = new StopWatch("StopWatch-Learning");
        String id = watch.getId();
        System.out.println("StopWatch 的id: " + id);
        // 启动任务,test为任务的名字
        watch.start("test");
        // 获取StopWatch是否正在运行
        boolean running = watch.isRunning();
        System.out.println("是否正在运行" + running);
        // 任务停止
        watch.stop();
        // 获取任务的名字
        String lastTaskName = watch.getLastTaskName();
        System.out.println("StopWatch任务的名字: " + lastTaskName);
        long lastTaskTimeNanos = watch.getLastTaskTimeNanos();// 任务运行时间 纳秒
//        long lastTaskTimeMillis = watch.getLastTaskTimeMillis(); // 任务运行时间 微妙
        System.out.println("任务" + lastTaskName + "运行了" + lastTaskTimeNanos + "ns");

    }

运行结果:

StopWatch 的id: StopWatch-Learning
是否正在运行true
StopWatch任务的名字: test
任务test运行了29000ns

我们还可以同时定义两个任务:

public static void main(String[] args) {
        // 构造方法的参数表示的是StopWatch实例的id,标志着实例的身份
        StopWatch watch = new StopWatch("StopWatch-Learning");
        watch.start("test1");
        // 任务停止
        watch.stop();
        // 获取任务的名字
        String lastTaskName = watch.getLastTaskName();
        long task1TimeNanos = watch.getLastTaskTimeNanos();// 任务运行时间 纳秒
//        long lastTaskTimeMillis = watch.getLastTaskTimeMillis(); // 任务运行时间 微妙
        System.out.println("task1" + lastTaskName + "运行了" + task1TimeNanos + "ns");


        watch.start("test2");
        watch.stop();
        long task2TimeNanos = watch.getLastTaskTimeNanos();
        System.out.println("task2" + lastTaskName + "运行了" + task2TimeNanos + "ns");
        
        int count = watch.getTaskCount();
        System.out.println("一共启动了" + count + "个任务");
        long totalTimeNanos = watch.getTotalTimeNanos();
        System.out.println("一共运行了" + totalTimeNanos + "ns");
}

运行结果:

task1test1运行了2200ns
task2test1运行了300ns
一共启动了2个任务
一共运行了2500ns
StopWatch 'StopWatch-Learning': running time = 2500 ns

对了还有一个方法prettyPrint(),这个方法可以帮助我们把输出的语句打印的更加好看!

public static void main(String[] args) {
        // 构造方法的参数表示的是StopWatch实例的id,标志着实例的身份
        StopWatch watch = new StopWatch("StopWatch-Learning");
        watch.start("test1");
        // 任务停止
        watch.stop();
        // 获取任务的名字
        String lastTaskName = watch.getLastTaskName();
        long task1TimeNanos = watch.getLastTaskTimeNanos();// 任务运行时间 纳秒
//        long lastTaskTimeMillis = watch.getLastTaskTimeMillis(); // 任务运行时间 微妙
        System.out.println("task1" + lastTaskName + "运行了" + task1TimeNanos + "ns");

        System.out.println("^^^^^^^^^^^^^^^^^^^^分割线^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
        String info = watch.prettyPrint();
        System.out.println(info);
    }

运行结果:

task1test1运行了2200ns
^^^^^^^^^^^^^^^^^^^^分割线^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
StopWatch 'StopWatch-Learning': running time = 2200 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
000002200  100%  test1

使用下来是不是很方便,丝滑,而且很清晰。

原理

我们看一下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();
	}

在任务开始的时候使用了 System.nanoTime()获取当前时间的纳秒时间戳,其实和我们之前使用System.currentTimeMillis()是一样的。

再来看一下stop()方法:

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;
	}

也是获取当前时间再减去开始时间,计算的时间差。和我们最开始说的currentTimeMillis本质上是一样的。只不过它的时间粒度更加细,是纳秒级别的。

突然有一种恍然大悟的感觉,这不和刚开始说的方法一样吗,不就是currentTimeMillis的改造版吗?但是就是因为改造的很好,我们可以很简单,很巧妙的计算任务的时间。

最巧妙的是我们可以给StopWatch创建一个id标志着它的身份,给每一个任务起一个名字,随时开始,停止,自动计算每一个任务,所有任务的时间差,还有一个自动格式化的结果的API,更加的美观,大气。在开发中对于多任务计算时长,很容易看错导致计算不准确,而StopWatch对于多任务优势就更加的明显。

既然StopWatch这么好用,还在等什么,还不用起来?今天的分享就到这里了,以后还会有更多的技术分享,欢迎大家的关注哦!

  • 作者:壹升茉莉清
  • 原文链接:https://blog.csdn.net/weixin_40920359/article/details/126038214
    更新时间:2023-04-25 18:55:56