Java計時新姿勢StopWatch的使用方法詳解
一、背景
有時我們在做開發(fā)的時候需要記錄每個任務執(zhí)行時間,或者記錄一段代碼執(zhí)行時間,最簡單的方法就是打印當前時間與執(zhí)行完時間的差值,一般我們檢測某段代碼執(zhí)行的時間,都是以如下方式來進行的:
public static void main(String[] args) { Long startTime = System.currentTimeMillis(); // 你的業(yè)務代碼 Long endTime = System.currentTimeMillis(); Long elapsedTime = (endTime - startTime) / 1000; System.out.println("該段總共耗時:" + elapsedTime + "s"); }
事實上該方法通過獲取執(zhí)行完成時間與執(zhí)行開始時間的差值得到程序的執(zhí)行時間,簡單直接有效,但想必寫多了也是比較煩人的,尤其是碰到不可描述的代碼時,會更加的讓人忍不住多寫幾個bug聊表敬意,而且如果想對執(zhí)行的時間做進一步控制,則需要在程序中很多地方修改。此時會想是否有一個工具類,提供了這些方法,剛好可以滿足這種場景?我們可以利用已有的工具類中的秒表,常見的秒表工具類有 org.springframework.util.StopWatch、org.apache.commons.lang.time.StopWatch以及谷歌提供的guava中的秒表(這個我沒怎么用過)。這里重點講下基于spring、Apache的使用
二、spring 用法
2.1 初遇
StopWatch 是位于 org.springframework.util 包下的一個工具類,通過它可方便的對程序部分代碼進行計時(ms級別),適用于同步單線程代碼塊。簡單總結(jié)一句,Spring提供的計時器StopWatch對于秒、毫秒為單位方便計時的程序,尤其是單線程、順序執(zhí)行程序的時間特性的統(tǒng)計輸出支持比較好。也就是說假如我們手里面有幾個在順序上前后執(zhí)行的幾個任務,而且我們比較關(guān)心幾個任務分別執(zhí)行的時間占用狀況,希望能夠形成一個不太復雜的日志輸出,StopWatch提供了這樣的功能。而且Spring的StopWatch基本上也就是僅僅為了這樣的功能而實現(xiàn)。
想要使用它,首先你需要在你的 Maven 中引入 Spring 核心包,當然 Spring MVC 和 Spring Boot 都已經(jīng)自動引入了該包:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency>
對一切事物的認知,都是從使用開始,那就先來看看它的用法,會如下所示:
public static void main(String[] args) throws InterruptedException { StopWatch stopWatch = new StopWatch(); // 任務一模擬休眠3秒鐘 stopWatch.start("TaskOneName"); Thread.sleep(1000 * 3); System.out.println("當前任務名稱:" + stopWatch.currentTaskName()); stopWatch.stop(); // 任務一模擬休眠10秒鐘 stopWatch.start("TaskTwoName"); Thread.sleep(1000 * 10); System.out.println("當前任務名稱:" + stopWatch.currentTaskName()); stopWatch.stop(); // 任務一模擬休眠10秒鐘 stopWatch.start("TaskThreeName"); Thread.sleep(1000 * 10); System.out.println("當前任務名稱:" + stopWatch.currentTaskName()); stopWatch.stop(); // 打印出耗時 System.out.println(stopWatch.prettyPrint()); System.out.println(stopWatch.shortSummary()); // stop后它的值為null System.out.println(stopWatch.currentTaskName()); // 最后一個任務的相關(guān)信息 System.out.println(stopWatch.getLastTaskName()); System.out.println(stopWatch.getLastTaskInfo()); // 任務總的耗時 如果你想獲取到每個任務詳情(包括它的任務名、耗時等等)可使用 System.out.println("所有任務總耗時:" + sw.getTotalTimeMillis()); System.out.println("任務總數(shù):" + sw.getTaskCount()); System.out.println("所有任務詳情:" + sw.getTaskInfo()); }
如圖所示,StopWatch 不僅正確記錄了上個任務的執(zhí)行時間,并且在最后還可以給出精確的任務執(zhí)行時間(納秒級別)和耗時占比,這或許就會比我們自己輸出要優(yōu)雅那么一些。
2.2 源碼
老規(guī)矩,由淺入深??赐暧梅?,我們來看看源碼。先看下組成 StopWatch 的屬性
public class StopWatch { /** * 本實例的唯一 Id,用于在日志或控制臺輸出時區(qū)分的。 */ private final String id; /** * 是否保持一個 taskList 鏈表 * 每次停止計時時,會將當前任務放入這個鏈表,用以記錄任務鏈路和計時分析 */ private boolean keepTaskList = true; /** * 任務鏈表 * 用來存儲每個task的信息, taskInfo由taskName 和 totoalTime組成 */ private final List<StopWatch.TaskInfo> taskList; /** * 當前任務的開始時間 */ private long startTimeMillis; /** * */ private boolean running; /** * 當前任務名稱 */ private String currentTaskName; /** * 最后一個任務的信息 */ private StopWatch.TaskInfo lastTaskInfo; /** * 任務總數(shù) */ private int taskCount; /** * 程序執(zhí)行時間 */ private long totalTimeMillis; ... }
接下來,我們看一下StopWatch類的構(gòu)造器和一些關(guān)鍵方法
方法 | 說明 |
---|---|
new StopWatch() | 構(gòu)建一個新的秒表,不開始任何任務。 |
new StopWatch(String id) | 創(chuàng)建一個指定了id的StopWatch |
String getId() | 返回此秒表的ID |
void start(String taskName) | 不傳入?yún)?shù),開始一個無名稱的任務的計時。 傳入String類型的參數(shù)來開始指定任務名的任務計時 |
void stop() | 停止當前任務的計時 |
boolean isRunning() | 是否正在計時某任務 |
String currentTaskName() | 當前正在運行的任務的名稱(如果有) |
long getTotalTimeMillis() | 所有任務的總體執(zhí)行時間(毫秒單位) |
double getTotalTimeSeconds() | 所有任務的總時間(以秒為單位) |
String getLastTaskName() | 上一個任務的名稱 |
long getLastTaskTimeMillis() | 上一個任務的耗時(毫秒單位) |
int getTaskCount() | 定時任務的數(shù)量 |
String shortSummary() | 總運行時間的簡短描述 |
String prettyPrint() | 優(yōu)美地打印所有任務的詳細耗時情況 |
2.3 注意事項
- StopWatch對象不是設計為線程安全的,并且不使用同步。
- 一個StopWatch實例一次只能開啟一個task,不能同時start多個task
- 在該task還沒stop之前不能start一個新的task,必須在該task stop之后才能開啟新的task
- 若要一次開啟多個,需要new不同的StopWatch實例
三、apache 用法
StopWath是 apache commons lang3 包下的一個任務執(zhí)行時間監(jiān)視器,與我們平時常用的秒表的行為比較類似,我們先看一下其中的一些重要方法:
方法 | 說明 |
---|---|
new StopWatch() | 構(gòu)建一個新的秒表,不開始任何任務。 |
static StopWatch createStarted() | |
void start() | 開始計時 |
void stop() | 停止當前任務的計時 |
void reset() | 重置計時 |
void split() | 設置split點 |
void unsplit() | |
void suspend() | 暫停計時, 直到調(diào)用resume()后才恢復計時 |
void resume() | 恢復計時 |
long getTime() | 統(tǒng)計從start到現(xiàn)在的計時 |
long getTime(final TimeUnit timeUnit) | |
long getNanoTime() | |
long getSplitTime() | 獲取從start 到 最后一次split的時間 |
long getSplitNanoTime() | |
long getStartTime() | |
boolean isStarted() | |
boolean isSuspended() | |
boolean isStopped() |
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.6</version> </dependency>
Apache提供的這個任務執(zhí)行監(jiān)視器功能豐富強大,靈活性強,如下經(jīng)典實用案例:
public static void main(String[] args) throws InterruptedException { //創(chuàng)建后立即start,常用 StopWatch watch = StopWatch.createStarted(); // StopWatch watch = new StopWatch(); // watch.start(); Thread.sleep(1000); System.out.println(watch.getTime()); System.out.println("統(tǒng)計從開始到現(xiàn)在運行時間:" + watch.getTime() + "ms"); Thread.sleep(1000); watch.split(); System.out.println("從start到此刻為止的時間:" + watch.getTime()); System.out.println("從開始到第一個切入點運行時間:" + watch.getSplitTime()); Thread.sleep(1000); watch.split(); System.out.println("從開始到第二個切入點運行時間:" + watch.getSplitTime()); // 復位后, 重新計時 watch.reset(); watch.start(); Thread.sleep(1000); System.out.println("重新開始后到當前運行時間是:" + watch.getTime()); // 暫停 與 恢復 watch.suspend(); System.out.println("暫停2秒鐘"); Thread.sleep(2000); // 上面suspend,這里要想重新統(tǒng)計,需要恢復一下 watch.resume(); System.out.println("恢復后執(zhí)行的時間是:" + watch.getTime()); Thread.sleep(1000); watch.stop(); System.out.println("花費的時間》》" + watch.getTime() + "ms"); // 直接轉(zhuǎn)成s System.out.println("花費的時間》》" + watch.getTime(TimeUnit.SECONDS) + "s"); }
四、java 中使用StopWatch來計算時間差
以前在進行時間耗時時我們通常的做法是先給出計算前后兩個的時間值,然后通過詳見來計算耗時時長。
eg:
long start = System.currentTimeMillis(); ......業(yè)務處理 System.out.println("耗時:" + (System.currentTimeMillis() - start) + "ms");
我們可以使用已有的工具類中的秒表來替代上述的使用方式,現(xiàn)有的秒表工具類有org.springframework.util.StopWatch、org.apache.commons.lang.time.StopWatch,這里以Spring的StopWatch類為例:
public static void main(String[] args) throws InterruptedException{ StopWatch stopWatch = new StopWatch("任務耗時秒表工具"); stopWatch.start("task1"); Thread.sleep(1000); stopWatch.stop(); System.out.println(stopWatch.getTotalTimeMillis()); stopWatch.start("task2"); Thread.sleep(3000); stopWatch.stop(); //所有任務耗時時間 System.out.println(stopWatch.getTotalTimeMillis()); System.out.println(stopWatch.prettyPrint()); StopWatch stopWatch2 = new StopWatch("任務耗時秒表工具2"); stopWatch2.start("task3"); Thread.sleep(3000); stopWatch2.stop(); //所有任務耗時時間 System.out.println(stopWatch2.getTotalTimeMillis()); System.out.println(stopWatch2.prettyPrint()); }
五、最后
很多時候,寫代碼也是一種藝術(shù),而借助這種實用工具我就覺得藝術(shù)感更強些。希望我們能有追求更加美好事物的心,這點對于接納新知識特別重要。此處推薦這個監(jiān)視器來代替之前的的使用,能讓小伙伴們更加靈活的分析你的代碼~
到此這篇關(guān)于Java計時新姿勢StopWatch使用的文章就介紹到這了,更多相關(guān)Java計時StopWatch內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于Mybatis中foreach遍歷Map的實現(xiàn)示例
這篇文章主要介紹了關(guān)于Mybatis中foreach遍歷Map的實現(xiàn)示例,MyBatis?是一款優(yōu)秀的半自動的ORM持久層框架,它支持自定義?SQL、存儲過程以及高級映射,需要的朋友可以參考下2023-05-05SpringBoot2 整合 ClickHouse數(shù)據(jù)庫案例解析
這篇文章主要介紹了SpringBoot2 整合 ClickHouse數(shù)據(jù)庫案例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-10-10java DateUtil工具類時間戳類型轉(zhuǎn)換詳解
這篇文章主要為大家詳細介紹了java DateUtil工具類時間戳類型轉(zhuǎn)換的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12如何在Intellij中安裝LeetCode刷題插件方便Java刷題
這篇文章主要介紹了如何在Intellij中安裝LeetCode刷題插件方便Java刷題,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08