前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java之StopWatch源码分析

java之StopWatch源码分析

作者头像
码农王同学
发布2019-10-24 23:15:34
9000
发布2019-10-24 23:15:34
举报
文章被收录于专栏:后端Coder后端Coder

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

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

代码语言:javascript
复制
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之间数字的总和,进行统计所消耗的时间,进行手动输出。

代码语言:javascript
复制
    public static native long currentTimeMillis();

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

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

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

代码语言:javascript
复制
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工具类提供的常用方法吧

代码语言:javascript
复制
我们看下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方法吧,我们继续看下下面的代码咯。

代码语言:javascript
复制
首先你在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。

代码语言:javascript
复制
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()进行信息格式友好的输出。

代码语言:javascript
复制
 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的内容到这里就讲完了,整个流程大致上就是常用方法的使用了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档