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