別再用 System.currentTimeMillis() 統(tǒng)計耗時了,太 Low,StopWatch 好用到爆!
背景
你還在用 System.currentTimeMillis... 統(tǒng)計耗時?
比如下面這段代碼:
/**
* @author: 棧長
* @from: 公眾號Java技術(shù)棧
*/
public void jdkWasteTime() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.printf("耗時:%dms.", System.currentTimeMillis() - start);
}
System.currentTimeMillis...這種方式統(tǒng)計耗時確實(shí)是用的最多的,因?yàn)樗挥靡肫渌?JAR 包,JDK 就能搞定,但是它用起來有幾個不方便的地方:
1)需要定義初始時間值,再用當(dāng)前時間進(jìn)行手工計算;
2)統(tǒng)計多個任務(wù)的耗時比較麻煩,如果 start 賦值搞錯可能還會出現(xiàn)邏輯問題;
有沒有其他的更好的替代方案呢?答案是肯定的:StopWatch!
StopWatch
StopWatch 是一個統(tǒng)計耗時的工具類:
常用的 StopWatch 工具類有以下兩種:
- commons-lang3(Apache 提供的通用工具包)
- spring-core(Spring 核心包)
雖然兩個工具類的名稱是一樣的,但是用法大不相同,本文棧長就給大家分別演示下。
commons-lang3 提供的 StopWatch
引入依賴
commons-lang3 是 Apache 開源的通用工具包,需要額外引入 Maven 依賴:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
簡單示例
創(chuàng)建一個 StopWatch 實(shí)例有以下 3 種方法:
1) 使用 new 關(guān)鍵字
StopWatch sw = new StopWatch();
2)使用 create 工廠方法
StopWatch sw = StopWatch.create();
3)使用 createStarted 方法
StopWatch sw = StopWatch.createStarted();
這個方法不但會創(chuàng)建一個實(shí)例,同時還會啟動計時。
來看一個簡單的例子:
// 創(chuàng)建一個 StopWatch 實(shí)例并開始計時
StopWatch sw = StopWatch.createStarted();
// 休眠1秒
Thread.sleep(1000);
// 1002ms
System.out.printf("耗時:%dms.\n", sw.getTime());
更多用法
接之前的示例繼續(xù)演示。
暫停計時:
// 暫停計時
sw.suspend();
Thread.sleep(1000);
// 1000ms
System.out.printf("暫停耗時:%dms.\n", sw.getTime());
因?yàn)闀和A?,所以還是 1000ms,暫停后中間休眠的 1000 ms 不會被統(tǒng)計。
恢復(fù)計時:
// 恢復(fù)計時
sw.resume();
Thread.sleep(1000);
// 2001ms
System.out.printf("恢復(fù)耗時:%dms.\n", sw.getTime());
因?yàn)榛謴?fù)了,結(jié)果是 2001 ms,恢復(fù)后中間休眠的 1000 ms 被統(tǒng)計了。
停止計時:
Thread.sleep(1000);
// 停止計時
sw.stop();
Thread.sleep(1000);
// 3009ms
System.out.printf("總耗時:%dms.\n", sw.getTime());
停止計時前休眠了 1000ms,所以結(jié)果是 3009ms,停止計時后就不能再使用暫停、恢復(fù)功能了。
重置計時:
// 重置計時
sw.reset();
// 開始計時
sw.start();
Thread.sleep(1000);
// 1000ms
System.out.printf("重置耗時:%dms.\n", sw.getTime());
因?yàn)橹刂糜嫊r了,所以重新開始計時后又變成了 1000ms。
Spring 提供的 StopWatch
來看一個簡單的例子:
// 創(chuàng)建一個 StopWatch 實(shí)例
StopWatch sw = StopWatch("公眾號Java技術(shù)棧:測試耗時");
// 開始計時
sw.start("任務(wù)1");
// 休眠1秒
Thread.sleep(1000);
// 停止計時
sw.stop();
// 1002ms
System.out.printf("任務(wù)1耗時:%d%s.\n", sw.getLastTaskTimeMillis(), "ms");
Spring 創(chuàng)建實(shí)例的方法就是 new,開始計時,以及獲取時間需要手動 start、stop。
繼續(xù)再新增 2 個任務(wù):
Thread.sleep(1000);
sw.start("任務(wù)2");
Thread.sleep(1100);
sw.stop();
// 1100ms.
System.out.printf("任務(wù)2耗時:%d%s.\n", sw.getLastTaskTimeMillis(), "ms");
sw.start("任務(wù)3");
Thread.sleep(1200);
sw.stop();
// 1203ms.
System.out.printf("任務(wù)3耗時:%d%s.\n", sw.getLastTaskTimeMillis(), "ms");
// 3.309373456s.
System.out.printf("任務(wù)數(shù)量:%s,總耗時:%ss.\n", sw.getTaskCount(), sw.getTotalTimeSeconds());
Spring 一個重要的亮點(diǎn)是支持格式化打印結(jié)果:
System.out.println(sw.prettyPrint());
來看最后的輸出結(jié)果:
不過有一點(diǎn)不友好的是,格式化結(jié)果顯示的是納秒,而且不能修改。。
實(shí)現(xiàn)原理
分別來看下 commons-lang3 和 Spring 的核心源碼:
其實(shí)也都是利用了 JDK 中的 System 系統(tǒng)類去實(shí)現(xiàn)的,做了一系列封裝而已。
總結(jié)
commons-lang3 工具包和 Spring 框架中的 StopWatch 都能輕松完成多個任務(wù)的計時以及總耗時,再也不要用手工計算耗時的方式了,手動計算如果 start 賦值錯誤可能還會出錯。
當(dāng)然,以上兩個 StopWatch 的功能也遠(yuǎn)不止棧長介紹的,棧長介紹的這些已經(jīng)夠用了,更多的可以深入研究。
本文所有完整示例源代碼已經(jīng)上傳:
- https://github.com/javastacks/javastack
歡迎 Star 學(xué)習(xí),后面 Java 示例都會在這上面提供!
總結(jié)一下這兩種計時工具類優(yōu)缺點(diǎn):
1)commons-lang3 中的 StopWatch 的用法比 Spring 中的要更簡單一些;
2)commons-lang3 中的 StopWatch 功能比 Spring 中的要更靈活、更強(qiáng)大一些,支持暫停、恢復(fù)、重置等功能;
3)Spring 提供每個子任務(wù)名稱,以及按格式化打印結(jié)果功能,針對多任務(wù)統(tǒng)計時更好一點(diǎn);
綜上所述,個人推薦使用 commons-lang3 工具包中的,更靈活、更強(qiáng)大,如果不想額外引入包,也可以考慮 Spring 中的,根據(jù)自己的系統(tǒng)需求定。
所以,別再用 System.currentTimeMillis... 統(tǒng)計耗時了,太 low,趕緊分享轉(zhuǎn)發(fā)下吧,規(guī)范起來!
好了,今天的分享就到這里了。