查看原文
其他

Guava 中的 Stopwatch 是个什么鬼?

程序猿DD 2021-05-27

点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

Stopwatch 解释为计时器,又称秒表、停表,很明显它是记录时间的。


# 如何使用


Stopwatch stopwatch = Stopwatch.createStarted(); doSomething(); stopwatch.stop(); // optional
long millis = stopwatch.elapsed(MILLISECONDS);// formatted string like "12.3 ms"}
log.info("time: " + stopwatch);

安卓使用:

Stopwatch.createStarted( new Ticker() { public long read() { return android.os.SystemClock.elapsedRealtime(); } });}

看了上面这段代码,有人会说,不用Stopwatch 照样可以实现执行时间的统计,比如:

long startTime = System.currentTimeMillis();
try { // 模拟业务逻辑 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(System.currentTimeMillis() - startTime);

确实是,这样也能统计这段代码的执行时间,那么为什么还会有Stopwatch(我也有这种想法)


官方称不直接使用System#nanoTime是有一下几个原因:


  • 时间源可以替代 可以重写Ticker(下面会介绍)

  • nanoTime的返回值是纳秒,返回的值没有意义,Stopwatch抽象返回值


下面从实现方式来分析下guava为什么会设计这么类


# 源码分析


内部有几个成员变量

//时间源 一般和Stopwatch一起使用,而不是单独使用 private final Ticker ticker; private boolean isRunning; private long elapsedNanos; private long startTick;

先看下Ticker(是个abstract类) 都有什么:

public static Ticker systemTicker() { return SYSTEM_TICKER; }
private static final Ticker SYSTEM_TICKER = new Ticker() { @Override public long read() { // 实际上就是System.nanoTime(); return Platform.systemNanoTime(); } }; // 子类重写    public abstract long read();

回到Stopwatch,看下它的构造方式:


public static Stopwatch createUnstarted() { return new Stopwatch(); }
/** * Creates (but does not start) a new stopwatch, using the specified time source. * * @since 15.0 */ public static Stopwatch createUnstarted(Ticker ticker) { return new Stopwatch(ticker); }
/** * Creates (and starts) a new stopwatch using {@link System#nanoTime} as its time source. * * @since 15.0 */ public static Stopwatch createStarted() { return new Stopwatch().start(); } Stopwatch() { this.ticker = Ticker.systemTicker(); }
Stopwatch(Ticker ticker) { this.ticker = checkNotNull(ticker, "ticker"); }

包括创建不启动,创建启动的构造方式


执行流程


start--> stop 或者 reset


看下代码,很简单

public Stopwatch start() { // 先判断是否处于执行状态 checkState(!isRunning, "This stopwatch is already running."); isRunning = true; // 初始化 当前的纳秒时间 startTick = ticker.read(); return this; }
public Stopwatch stop() { long tick = ticker.read(); checkState(isRunning, "This stopwatch is already stopped."); isRunning = false; elapsedNanos += tick - startTick; return this; } public Stopwatch reset() { elapsedNanos = 0; isRunning = false; return this;  }

获取结果的代码:

// 计算纳秒 private long elapsedNanos() { return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos; }
// 转换其他单位 public long elapsed(TimeUnit desiredUnit) { return desiredUnit.convert(elapsedNanos(), NANOSECONDS); }
还有一些单位转换和toString方法,就不分析了


# 总结


  • 支持TimeUnit,可以将计算后的时间转换为各种单位 比如:stopwatch.elapsed(TimeUnit.SECONDS))

  • 同一个Stopwatch,可以重置,重复记录

  • 时间源可以替代 可以重写Ticker

  • 其他 Spring 也有StopWatch 实现方式差不多,不支持替换时间源和可以重置,支持毫秒和纳秒,但是增加了Task的概念

来源:https://my.oschina.net/lowkeysoft/blog/1595755


往期推荐

为什么 Redis 单线程能支撑高并发?

华为又一战略级生态启程:华为IdeaHub 使能千行百业

必须了解的 MySQL 三大日志

说了低调...这下百度知道了...

离职半年了,最近又开始被吐槽输出不够...


扫一扫,关注我

一起学习,一起进步

每周赠书,福利不断

深度内容

推荐加入


最近热门内容回顾   #技术人系列

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存