java之StopWatch源码分析

计时这个词语在生活中被应用的很普遍,比如,哥们,帮我计时一下在山的那边,海的那边,.........,这好像是一首歌的歌词,坏笑。跑过去需要多长时间?

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

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

Tips:左右滑动可以显示完整代码。

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

Tips:左右滑动可以看完整代码

首先你在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的内容到这里就讲完了,整个流程大致上就是常用方法的使用了,看到这篇文章的你,很高兴在这里遇到你,喜欢我的文章的可以关注下,转发,分享下。

觉得文章需要纠正的点可以留言哈,最近开通了赞赏功能,喜欢文章的,可以给个赞赏哈,感谢你的阅读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值