前言
编码过程中我们经常会希望得到一段代码(一个方法)的执行时间,本文将介绍两种时间监视器(秒表)来让你优雅的、灵活的处理这个问题。
Java源生方式
这种方式最最简单,最好理解,当然也是最为常用:我们自己书写。
例如:我们如果要统计一段代码的执行时间,经常会这么来写:
public static void main(String[] args) { long startTime = System.currentTimeMillis(); //获取开始时间 //函数主体代码 //... long endTime = System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间: " + (endTime - startTime) + "ms"); }
大多数时候我们使用ms来表示即可,但是这么写缺乏灵活性。倘若我们要展示成纳秒、秒、甚至分钟,还得我们自己处理(把毫秒值拿来进行转换~ )
当然可能到了JDK8
以后,我们这么做能变得稍微灵活一些:可以这么处理:
public static void main(String[] args) { Instant start = Instant.now(); //doSomething(); Instant end = Instant.now(); Duration duration = Duration.between(start, end); System.out.println("millis = " + duration.toMillis()); }
这个比上面灵活度强一些。但也还是有一定的缺点:步骤稍显复杂,总体上还是不够优雅,也不是那么的灵活。
那么本文针对此问题介绍一个工具:StopWatch执行时间监视器。借助它来统计我们程序的执行时间,带给非常多的方便和优雅。
StopWatch需要依赖额外的Jar:commons-lang3或者spring-core,但因这两个Jar是Java开发中都必导的,因此依赖兼容性方面可以忽略
StopWatch有很多开源的框架都有提供类似的功能:比如Apache的commons-lang3,当然还有Spring framwork自己提供的,本文将针对此俩分别做介绍~
Commons-lang3的StopWatch
Apache提供的这个任务执行监视器功能丰富强大(比Spring的强大),灵活性强,如下经典实用案例:
public static void main(String[] args) throws Exception { StopWatch watch = StopWatch.createStarted(); //创建后立即start,常用 //StopWatch watch = new StopWatch(); //watch.start(); Thread.sleep(1000); System.out.println("统计从开始到现在运行时间:" + watch.getTime() + "ms"); //1000ms Thread.sleep(1000); watch.split(); System.out.println("从start到此刻为止的时间:" + watch.getTime()); System.out.println("从开始到第一个切入点运行时间:" + watch.getSplitTime()); //2245 Thread.sleep(1000); watch.split(); System.out.println("从开始到第二个切入点运行时间:" + watch.getSplitTime()); watch.reset(); //重置后必须使用start方法 watch.start(); Thread.sleep(1000); System.out.println("重新开始后到当前运行时间是:" + watch.getTime()); //1000 watch.suspend(); //暂停 Thread.sleep(6000); //模拟暂停6秒钟 watch.resume(); //上面suspend,这里要想重新统计,需要恢复一下 System.out.println("恢复后执行的时间是:" + watch.getTime()); //1000 注意此时这个值还是1000 watch.stop(); System.out.println("花费的时间》》" + watch.getTime() + "ms"); //1002ms System.out.println("花费的时间》》" + watch.getTime(TimeUnit.SECONDS) + "s"); //1s 可以直接转成s }
打印结果:
统计从开始到现在运行时间:1007ms 从start到此刻为止的时间:2008 从开始到第一个切入点运行时间:2008 从开始到第二个切入点运行时间:3009 重新开始后到当前运行时间是:1000 恢复后执行的时间是:1000 花费的时间》》1001ms 花费的时间》》1s
如上就是StopWatch
的基本使用方法,足以见到了它的强大吧,当然使用起来复杂度也是提升了些的。
核心原理解释
原理相对简单,简单看看源码便知:
// @since 2.0 public class StopWatch { // @since 3.5 这个静态方法出现得稍微晚点哦~ public static StopWatch createStarted() { final StopWatch sw = new StopWatch(); sw.start(); return sw; } // 这些成员变量是实现的核心~~~~~~~~~~~~~~ private State runningState = State.UNSTARTED; private SplitState splitState = SplitState.UNSPLIT; private long startTime; // 思考:为何有了nonaTime这里还得记录一个Millis Time呢??? // 因为nanoTime只能拿来计算差值(耗时) 但是getStartTime()这个老API还得靠MillsTime~~~ private long startTimeMillis; private long stopTime; // 可见:start方法可不是能够多次调用的哦~~和状态是有关的 public void start() { if (this.runningState == State.STOPPED) { throw new IllegalStateException("Stopwatch must be reset before being restarted. "); } if (this.runningState != State.UNSTARTED) { throw new IllegalStateException("Stopwatch already started. "); } this.startTime = System.nanoTime(); this.startTimeMillis = System.currentTimeMillis(); this.runningState = State.RUNNING; } // 停表时,最重要的是记录下了stopTime 的值~~~然后标记状态 public void stop() { if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) { throw new IllegalStateException("Stopwatch is not running. "); } if (this.runningState == State.RUNNING) { this.stopTime = System.nanoTime(); } this.runningState = State.STOPPED; } // 状态变为非开始状态... public void reset() { this.runningState = State.UNSTARTED; this.splitState = SplitState.UNSPLIT; } // 暂停:stopTime 也给了一个值 public void suspend() { if (this.runningState != State.RUNNING) { throw new IllegalStateException("Stopwatch must be running to suspend. "); } this.stopTime = System.nanoTime(); this.runningState = State.SUSPENDED; } // 这两个方法是获取差值的 public long getTime() { return getNanoTime() / NANO_2_MILLIS; } // @since 3.5 public long getTime(final TimeUnit timeUnit) { return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS); } // @since 2.4 老API 这叫获取启动的时间(啥时候启动的) public long getStartTime() { if (this.runningState == State.UNSTARTED) { throw new IllegalStateException("Stopwatch has not been started"); } // System.nanoTime is for elapsed time return this.startTimeMillis; } }
可以看到原理是很简单的,无非就是包装了暂停、回复、split等功能嘛