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这么好用,还在等什么,还不用起来?今天的分享就到这里了,以后还会有更多的技术分享,欢迎大家的关注哦!