专栏首页后端Coderjava之StopWatch源码分析

java之StopWatch源码分析

计时这个词语在生活中被应用的很普遍,体育竞赛时频繁出现的秒表,发令信号一经发出,秒表就在滴答滴答流转开始计时了,秒表此时的作用就是计时的代名词,在我们编写代码的时候,时不时也要统计一下执行一个方法或者一系列逻辑时所消耗的时间。

这时我们就用到了java常用的计时方式之一了,System.currentTime()方法在方法的前后计算时间差。配上代码的方式我们来看下。

public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        final long size = 1000000000;
        long sum = 0;
        for (int i = 0; i < size; i++) {
            sum += i;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("统计计算0~1000000000相加的总和" + sum + "耗时时间为:" + (endTime - startTime) + "ms");
    }

我们先分析这段代码涉及的内容吧,计算0到1000000000之间数字的总和,进行统计所消耗的时间,进行手动输出。

    public static native long currentTimeMillis();

上面的方法使用native关键字进行标识,这不是一个java方法,而是一个本地方法,本地方法是运行在本地方法栈的。

想了解本地方法栈和java方法栈的内容可以先看下这两篇文章java虚拟机,应该了解一点点,另外一篇是java内存区域划分详解,后面的一篇是对前面内容的详细描述吧,希望可以帮助到你,喜欢的可以关注此公众号,转发,分享一下。

我们继续说下另外一种计时方法吧,StopWatch,这是spring框架提供的一个工具类,只要你使用了spring框架,就无需导入其它jar包了,好了,我们看下它的常见用法吧,继续按照我们一贯的风格,先将代码看下。

public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("统计0~1000000000相加的总和所需时间");
        final long size = 1000000000;
        long sum = 0;
        for (int i = 0; i < size; i++) {
            sum += i;
        }
        stopWatch.stop();
        //下面我们使用stopWatch提供的方法进行信息的输出
        System.out.println(stopWatch.prettyPrint());
    }

StopWatch的使用,先手动new一个对象,通过这个对象我们可以调用里面的实例方法了。

下面我们继续看下StopWatch工具类提供的常用方法吧

我们看下stopWatch方法做了什么,和原来的有什么不一样的,除了多了给当前的执行
任务起了一个有明确含义的任务名,在计算时间时任然时调用的是System.currentTimeMillis()方法
public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
            throw new IllegalStateException("Can't start StopWatch: it's already running");
        } else {
            this.currentTaskName = taskName;
            this.startTimeMillis = System.currentTimeMillis();
        }
 }

好了,上面的方法,大致上你应该明白其中的含义了,下面我们看下StopWatch是如何帮我们计算时间差的stop方法吧,我们继续看下下面的代码咯。

首先你在new StopWatch()对象时可以设置taskName,不过我们设置名字建议要设置
一个有意义的名字,不然后续出现问题时,定位问题很麻烦,其实stop()方法
在内部也是按照传统的计算时间差的方法,看下int lastTime=System.currentTimeMillis() - this.startTimeMillis;
和我们自己设置没什么两样。
public void stop() throws IllegalStateException {
        if (this.currentTaskName == null) {
            throw new IllegalStateException("Can't stop StopWatch: it's not running");
        } else {
            long lastTime = System.currentTimeMillis() - this.startTimeMillis;
            this.totalTimeMillis += lastTime;
            this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
            if (this.keepTaskList) {
                this.taskList.add(this.lastTaskInfo);
            }
            ++this.taskCount;
            this.currentTaskName = null;
        }
 }

上面的代码的第11行通过stopWatch实例new了一个taskInfo对象,我们看下这个静态内部类的代码吧,go on。

public static final class TaskInfo {
        private final String taskName;
        private final long timeMillis;
        TaskInfo(String taskName, long timeMillis) {
            this.taskName = taskName;
            this.timeMillis = timeMillis;
        }
        public String getTaskName() {
            return this.taskName;
        }
        public long getTimeMillis() {
            return this.timeMillis;
        }
        public double getTimeSeconds() {
            return (double)this.timeMillis / 1000.0D;
        }
    }

这个静态内部类的主要作用是记录当前任务的名称和时间的,将其封装成一个对象,为下面的流程做下铺垫的。

在stop()方法的代码里面第13行将taskInfo对象信息装入的集合中,这样当有多个任务时,我们打印任务集合信息时就可以了,很方便。

下面再分析一下这个方法了,在我们的示例中,我们使用了下面的这个方法prettyPrint()进行信息格式友好的输出。

 public String shortSummary() {
        return "StopWatch '" + this.getId() + "': running time (millis) = " + this.getTotalTimeMillis();
 }
public String prettyPrint() {
//调用上面的方法,在信息头部输出计时所耗时间
        StringBuilder sb = new StringBuilder(this.shortSummary());
        //使用StringBuilder进行字符串信息的拼接
        sb.append('\n');
        if (!this.keepTaskList) {
            sb.append("No task info kept");
        } else {
            sb.append("-----------------------------------------\n");
            sb.append("ms     %     Task name\n");
            sb.append("-----------------------------------------\n");
            NumberFormat nf = NumberFormat.getNumberInstance();
            nf.setMinimumIntegerDigits(5);
            nf.setGroupingUsed(false);
            NumberFormat pf = NumberFormat.getPercentInstance();
            pf.setMinimumIntegerDigits(3);
            pf.setGroupingUsed(false);
            //下面就是将你的程序写入的任务信息进行输出
            StopWatch.TaskInfo[] var4 = this.getTaskInfo();
            int var5 = var4.length;
            for(int var6 = 0; var6 < var5; ++var6) {
                StopWatch.TaskInfo task = var4[var6];
                sb.append(nf.format(task.getTimeMillis())).append("  ");
                sb.append(pf.format(task.getTimeSeconds() / this.getTotalTimeSeconds())).append("  ");
                sb.append(task.getTaskName()).append("\n");
            }
        }
        return sb.toString();
    }

StopWatch的内容到这里就讲完了,整个流程大致上就是常用方法的使用了。

本文分享自微信公众号 - WwpwW(gh_245290c1861a),作者:后端Coder

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java之Builder模式的设计实现

    之所以会写这篇文章,其实是有原因的,自己整理了一篇文章是关于Lombok的介绍和使用的文章,它里面涉及到了今天我们想要分享的设计模式Builder模式。

    后端Coder
  • java自定义注解的使用

    不曾想,每个人都是这样经历过来的,不知你是否还记得在spring的xml配置文件里如何配置对象的场景?或许依然记忆犹新,或许早已抛开在脑后,等等吧。后面spri...

    后端Coder
  • 聊聊同步与异步的理解

    写过几篇关于同步与异步相关代码的文章,不知你是否感受到了里面的含义,今天以个人见解谈谈我对同步与异步的理解。

    后端Coder
  • GD实战开发验证码

    GD GD库是php处理图形的扩展库,GD库提供了一系列用来处理图片的API,在网站上GD库通常用来生成缩略图,或者用来对图片加水印,或者用来生成汉字验证码,或...

    benny
  • 聊聊rocketmq的ScheduleMessageService

    rocketmq-all-4.6.0-source-release/store/src/main/java/org/apache/rocketmq/store/...

    codecraft
  • 聊聊rocketmq的ScheduleMessageService

    rocketmq-all-4.6.0-source-release/store/src/main/java/org/apache/rocketmq/store/...

    codecraft
  • 用 SOLID 原则保驾 React 组件开发

    本世纪初,美国计算机专家和作者 Robert Cecil Martin 针对 OOP 编程,提出了可以很好配合的五个独立模式;后由重构等领域的专家 Michae...

    江米小枣
  • 箭头函数中的'this'值

    那么 为什么hobby的值输出成功,而name不能够输出呢?是因为lucifer丑吗?其实那只是其中一个因素,还有一个因素就是在ZnHobbies方法中...

    学前端
  • JS实现简易的计算器

    自认脑袋不够大,就实现一个普通版本的吧(支持正负数加减乘除等基本连续的运算,未提供括号功能)

    书童小二
  • (必考)关于JS中this关键字的说法,下面错误的是?

    当一个函数被调用时,拥有它的object会作为this传入。在global下,就是window or global,其他时候就是相应的object。

    舒克

扫码关注云+社区

领取腾讯云代金券