还在用System.currentTimeMillis()?深入浅出Stopwatch计时工具类

1,741 阅读2分钟

一、简介

StopWatch是java中的一个类库,翻译成中文是秒表的意思,顾名思义它的功能就是计时。

如上图,可见有很多个实现类库,api有些许差别,com.google.common.base.Stopwatch相比之下一些api更加灵活,因此项目中选用的是该Stopwtach。

二、为什么用StopWatch

计时功能常常在日志中用到,之前我们常用到的是:System.currentTimeMillis();

这里举例一个计算方法执行时间功能,代码如下:

    @Test
    public void test1() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end1 = System.currentTimeMillis();
        System.out.println((start - end1) / 1000);
        Thread.sleep(1000);
        long end2 = System.currentTimeMillis();
        System.out.println((start - end2) / 1000);
    }

相比之下StopWatch更加优雅:

    @Test
    public void test2() throws InterruptedException {
        Stopwatch started = Stopwatch.createStarted();
        Thread.sleep(1000);
        System.out.println(started.elapsed(TimeUnit.SECONDS));
        Thread.sleep(1000);
        System.out.println(started.elapsed(TimeUnit.SECONDS));
    }

就好像秒表按很多下计时键一样,统计出执行到每个步骤时消耗的时间。

三、怎么用

1.获取实例

获取Stopwatch有两种方式,分别是createUnstarted和createStarted

  • createUnstarted:只是获取一个Stopwatch实例
  • createStarted(常用) :获取一个Stopwatch实例,并立即开始计时

2.elapsed(TimeUnit desiredUnit)

返回Stopwatch经过的时间(可以指定返回的时间单位)

3.isRunning

判断计时器是否在运行状态

4.start

启动Stopwatch

5.stop

停止Stopwatch

6.reset

时间设置为0,状态设置为静止

四、源码

1.几个成员变量

//时间源 
private final Ticker ticker;
//运行状态
private boolean isRunning;
//计时器stop前记录的时间
private long elapsedNanos;
//计时器开启时的时间
private long startTick;

2.Ticker类

看上去没有什么可以灵活配置的地方

public abstract class Ticker {

  protected Ticker() {}

  public abstract long read();

  @CheckReturnValue
  public static Ticker systemTicker() {
    return SYSTEM_TICKER;
  }

  private static final Ticker SYSTEM_TICKER =
      new Ticker() {
        @Override
        public long read() {
          //实现就是System.nanoTime();
          return Platform.systemNanoTime();
        }
      };
}

3.实例创建

即调用工厂方法实例化一个Ticker赋值自己的实例变量ticker,started方法直接调用start方法。

  public static Stopwatch createStarted() {
    return new Stopwatch().start();
  }

  public static Stopwatch createUnstarted() {
    return new Stopwatch();
  }

  Stopwatch() {
    this.ticker = Ticker.systemTicker();
  }

4.start

  public Stopwatch start() {
    checkState(!isRunning, "This stopwatch is already running.");
    //计时器标记为启动
    isRunning = true;
    //获取当前系统时间作为startTick
    startTick = ticker.read();
    return this;
  }

5.stop

  public Stopwatch stop() {
    //获取当前系统时间
    long tick = ticker.read();
    checkState(isRunning, "This stopwatch is already stopped.");
    //计时器标记为停止
    isRunning = false;
    //记录一下暂停前记录的时间
    elapsedNanos += tick - startTick;
    return this;
  }

6.elapsed

  public long elapsed(TimeUnit desiredUnit) {
    //把时间转换为制定的单位
    return desiredUnit.convert(elapsedNanos(), NANOSECONDS);
  }

  //最后返回的就是当前计时的时间
  private long elapsedNanos() {
    //如果在运行,返回:(当前时间-最近一次开始时间)+最近一次开始前记录的时间
    //如果不在运行,返回记录的暂停前记录的时间
    return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos;
  }

7.reset

  public Stopwatch reset() {
    //暂停前记录的时间置为0
    elapsedNanos = 0;
    //运行状态置为停止
    isRunning = false;
    return this;
  }

五、参考资料

GUAVA STOPWATCH源码解析