摘要:借助它來統(tǒng)計我們程序的執(zhí)行時間,帶給非常多的方便和優(yōu)雅。且,且,且設(shè)置為了當前時間。
相關(guān)閱讀
【小家java】java5新特性(簡述十大新特性) 重要一躍
【小家java】java6新特性(簡述十大新特性) 雞肋升級
【小家java】java7新特性(簡述八大新特性) 不溫不火
【小家java】java8新特性(簡述十大新特性) 飽受贊譽
【小家java】java9新特性(簡述十大新特性) 褒貶不一
【小家java】java10新特性(簡述十大新特性) 小步迭代
【小家java】java11新特性(簡述八大新特性) 首個重磅LTS版本
編碼過程中我們經(jīng)常會希望得到一段代碼(一個方法)的執(zhí)行時間,本文將介紹兩種時間監(jiān)視器(秒表)來讓你優(yōu)雅的、靈活的處理這個問題。
Java源生方式這種方式最最簡單,最好理解,當然也是最為常用:我們自己書寫。
例如:我們?nèi)绻y(tǒng)計一段代碼的執(zhí)行時間,經(jīng)常會這么來寫:
public static void main(String[] args) { long startTime = System.currentTimeMillis(); //獲取開始時間 //函數(shù)主體代碼 //... long endTime = System.currentTimeMillis(); //獲取結(jié)束時間 System.out.println("程序運行時間: " + (endTime - startTime) + "ms"); }
大多數(shù)時候我們使用ms來表示即可,但是這么寫缺乏靈活性。倘若我們要展示成納秒、秒、甚至分鐘,還得我們自己處理(把毫秒值拿來進行轉(zhuǎn)換~ )
當然可能到了JDK8以后,我們這么做能變得稍微靈活一些:可以這么處理:
public static void main(String[] args) { Instant start = Instant.now(); //doSomething(); Instant end = Instant.now(); Duration duration = Duration.between(start, end); System.out.println("millis = " + duration.toMillis()); }
這個比上面靈活度強一些。但也還是有一定的缺點:步驟稍顯復雜,總體上還是不夠優(yōu)雅,也不是那么的靈活。
那么本文針對此問題介紹一個工具:StopWatch執(zhí)行時間監(jiān)視器。借助它來統(tǒng)計我們程序的執(zhí)行時間,帶給非常多的方便和優(yōu)雅。
StopWatch需要依賴額外的Jar:commons-lang3或者spring-core,但因這兩個Jar是Java開發(fā)中都必導的,因此依賴兼容性方面可以忽略
StopWatch有很多開源的框架都有提供類似的功能:比如Apache的commons-lang3,當然還有Spring framwork自己提供的,本文將針對此倆分別做介紹~
Commons-lang3的StopWatchApache提供的這個任務執(zhí)行監(jiān)視器功能豐富強大(比Spring的強大),靈活性強,如下經(jīng)典實用案例:
public static void main(String[] args) throws Exception { StopWatch watch = StopWatch.createStarted(); //創(chuàng)建后立即start,常用 //StopWatch watch = new StopWatch(); //watch.start(); Thread.sleep(1000); System.out.println("統(tǒng)計從開始到現(xiàn)在運行時間:" + watch.getTime() + "ms"); //1000ms Thread.sleep(1000); watch.split(); System.out.println("從start到此刻為止的時間:" + watch.getTime()); System.out.println("從開始到第一個切入點運行時間:" + watch.getSplitTime()); //2245 Thread.sleep(1000); watch.split(); System.out.println("從開始到第二個切入點運行時間:" + watch.getSplitTime()); watch.reset(); //重置后必須使用start方法 watch.start(); Thread.sleep(1000); System.out.println("重新開始后到當前運行時間是:" + watch.getTime()); //1000 watch.suspend(); //暫停 Thread.sleep(6000); //模擬暫停6秒鐘 watch.resume(); //上面suspend,這里要想重新統(tǒng)計,需要恢復一下 System.out.println("恢復后執(zhí)行的時間是:" + watch.getTime()); //1000 注意此時這個值還是1000 watch.stop(); System.out.println("花費的時間》》" + watch.getTime() + "ms"); //1002ms System.out.println("花費的時間》》" + watch.getTime(TimeUnit.SECONDS) + "s"); //1s 可以直接轉(zhuǎn)成s }
打印結(jié)果:
統(tǒng)計從開始到現(xiàn)在運行時間:1007ms 從start到此刻為止的時間:2008 從開始到第一個切入點運行時間:2008 從開始到第二個切入點運行時間:3009 重新開始后到當前運行時間是:1000 恢復后執(zhí)行的時間是:1000 花費的時間》》1001ms 花費的時間》》1s
如上就是StopWatch的基本使用方法,足以見到了它的強大吧,當然使用起來復雜度也是提升了些的。
核心原理解釋原理相對簡單,簡單看看源碼便知:
// @since 2.0 public class StopWatch { // @since 3.5 這個靜態(tài)方法出現(xiàn)得稍微晚點哦~ public static StopWatch createStarted() { final StopWatch sw = new StopWatch(); sw.start(); return sw; } // 這些成員變量是實現(xiàn)的核心~~~~~~~~~~~~~~ private State runningState = State.UNSTARTED; private SplitState splitState = SplitState.UNSPLIT; private long startTime; // 思考:為何有了nonaTime這里還得記錄一個Millis Time呢??? // 因為nanoTime只能拿來計算差值(耗時) 但是getStartTime()這個老API還得靠MillsTime~~~ private long startTimeMillis; private long stopTime; // 可見:start方法可不是能夠多次調(diào)用的哦~~和狀態(tài)是有關(guān)的 public void start() { if (this.runningState == State.STOPPED) { throw new IllegalStateException("Stopwatch must be reset before being restarted. "); } if (this.runningState != State.UNSTARTED) { throw new IllegalStateException("Stopwatch already started. "); } this.startTime = System.nanoTime(); this.startTimeMillis = System.currentTimeMillis(); this.runningState = State.RUNNING; } // 停表時,最重要的是記錄下了stopTime 的值~~~然后標記狀態(tài) public void stop() { if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) { throw new IllegalStateException("Stopwatch is not running. "); } if (this.runningState == State.RUNNING) { this.stopTime = System.nanoTime(); } this.runningState = State.STOPPED; } // 狀態(tài)變?yōu)榉情_始狀態(tài)... public void reset() { this.runningState = State.UNSTARTED; this.splitState = SplitState.UNSPLIT; } // 暫停:stopTime 也給了一個值 public void suspend() { if (this.runningState != State.RUNNING) { throw new IllegalStateException("Stopwatch must be running to suspend. "); } this.stopTime = System.nanoTime(); this.runningState = State.SUSPENDED; } // 這兩個方法是獲取差值的 public long getTime() { return getNanoTime() / NANO_2_MILLIS; } // @since 3.5 public long getTime(final TimeUnit timeUnit) { return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS); } // @since 2.4 老API 這叫獲取啟動的時間(啥時候啟動的) public long getStartTime() { if (this.runningState == State.UNSTARTED) { throw new IllegalStateException("Stopwatch has not been started"); } // System.nanoTime is for elapsed time return this.startTimeMillis; } }
可以看到原理是很簡單的,無非就是包裝了暫停、回復、split等功能嘛
使用細節(jié)==getTime和getSplitTime有啥區(qū)別呢?==
為了說明問題,此處我們看看getNanoTime()和getSplitNanoTime()亦可:
public long getNanoTime() { if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) { return this.stopTime - this.startTime; } else if (this.runningState == State.UNSTARTED) { return 0; } else if (this.runningState == State.RUNNING) { return System.nanoTime() - this.startTime; } throw new RuntimeException("Illegal running state has occurred."); } public long getSplitNanoTime() { if (this.splitState != SplitState.SPLIT) { throw new IllegalStateException("Stopwatch must be split to get the split time. "); } return this.stopTime - this.startTime; }
我們發(fā)現(xiàn):
調(diào)用getSplit...相關(guān)方法前,必須先調(diào)用Split方法
spilit()方法源碼如下:
public void split() { if (this.runningState != State.RUNNING) { throw new IllegalStateException("Stopwatch is not running. "); } this.stopTime = System.nanoTime(); this.splitState = SplitState.SPLIT; }
在調(diào)用split方法后,watch的狀態(tài)改為了SPLIT。且,且,且stopTime 設(shè)置為了當前時間。因此此處我們的stopTime停止了,這個時候調(diào)用getSplitNanoTime(),返回的是start到split那時的時間差值。因此用此方法可以插入先停止stopTime()(有點插隊的趕腳),最后再輸出(先插好隊,最后在輸出)~
而getTime()就是拿當前的時間戳,減去startTime,一般不涉及到stopTime的值,因此splitTime處理計算時間顯然更加的靈活,但是,一般我們使用getTime()就足夠了
Spring的StopWatchSpring提供的這個任務監(jiān)視器,我還是蠻喜歡使用的,因為一個它能夠幫我同事監(jiān)控多個任務,使用起來也很方便。先看一個簡單的使用案例:
注意:一個監(jiān)視器能夠記錄多個任務的執(zhí)行時間這個特點非常重要哦~
比如:我們可以記錄多段代碼耗時時間,然后一次性打印~
public static void main(String[] args) throws Exception { // 強烈每一個秒表都給一個id,這樣查看日志起來能夠更加的精確 // 至于Id 我覺得給UUID是可行的~ StopWatch sw = new StopWatch(UUID.randomUUID().toString()); sw.start("起床"); Thread.sleep(1000); System.out.println("當前任務名稱:" + sw.currentTaskName()); sw.stop(); sw.start("洗漱"); Thread.sleep(2000); System.out.println("當前任務名稱:" + sw.currentTaskName()); sw.stop(); sw.start("鎖門"); Thread.sleep(500); System.out.println("當前任務名稱:" + sw.currentTaskName()); sw.stop(); System.out.println(sw.prettyPrint()); // 這個方法打印在我們記錄日志時是非常友好的 還有百分比的分析哦 System.out.println(sw.shortSummary()); System.out.println(sw.currentTaskName()); // stop后它的值為null // 最后一個任務的相關(guān)信息 System.out.println(sw.getLastTaskName()); System.out.println(sw.getLastTaskInfo()); // 任務總的耗時 如果你想獲取到每個任務詳情(包括它的任務名、耗時等等)可使用 System.out.println("所有任務總耗時:" + sw.getTotalTimeMillis()); System.out.println("任務總數(shù):" + sw.getTaskCount()); System.out.println("所有任務詳情:" + sw.getTaskInfo()); // 拿到所有的任務 }
打印:
當前任務名稱:起床 當前任務名稱:洗漱 當前任務名稱:鎖門 StopWatch "d6ba9412-d551-4ba7-8b0e-1b7ccb42855d": running time (millis) = 3504 ----------------------------------------- ms % Task name ----------------------------------------- 01001 029% 起床 02000 057% 洗漱 00503 014% 鎖門 StopWatch "d6ba9412-d551-4ba7-8b0e-1b7ccb42855d": running time (millis) = 3504 null 鎖門 org.springframework.util.StopWatch$TaskInfo@2d554825 所有任務總耗時:3504 任務總數(shù):3 所有任務詳情:[Lorg.springframework.util.StopWatch$TaskInfo;@68837a77
我個人偏愛使用Spring提供的這個監(jiān)視器,是因為它提供的prettyPrint()打印在日志里進行分析可以非常的直觀,并且我覺得提供的多任務支持也更加實用一點,當然僅僅個人偏好而已~
最后很多時候,寫代碼也是一種藝術(shù),而借助這種實用工具我就覺得藝術(shù)感更強些。希望我們能有追求更加美好事物的心,這點對于接納新知識特別重要。此處推薦這個監(jiān)視器來代替之前的的使用,能讓小伙伴們更加靈活的分析你的代碼~
知識交流若文章格式混亂,可點擊:原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接
==The last:如果覺得本文對你有幫助,不妨點個贊唄。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==
**若對技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群。
若群二維碼失效,請加wx號:fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會手動邀請入群**
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75636.html
摘要:一般在存當前含有當前時間的實體時,只需要配置好數(shù)據(jù)庫的存儲字段即可。基本代碼部分循環(huán)的寫法 這幾天初步了解了百度云的后臺架構(gòu)部分,當然了,自己了解的僅限于后臺java相關(guān)的部分,先說一下客戶端這邊使用的技術(shù):1、spring boot : 與前端進行直接交互的服務是用spring來實現(xiàn)的(后臺服務還需要調(diào)用其他的基礎(chǔ)服務,如redis 數(shù)據(jù)庫服務 訂單服務 cdn服務 openstac...
摘要:介紹它是出品,最流行的,能力強勁的開源消息總線。是一個完全支持和規(guī)范的實現(xiàn),盡管規(guī)范出臺已經(jīng)是很久的事情了,但是在當今的應用中間仍然扮演著特殊的地位。相關(guān)文章整合使用整合使用關(guān)注我轉(zhuǎn)載請務必注明原創(chuàng)地址為安裝同之前一樣,直接在里面玩吧。 showImg(https://segmentfault.com/img/remote/1460000012996066?w=1920&h=1281)...
摘要:接上一小節(jié)徹底征服之理論篇實戰(zhàn)看了上面這么多的理論知識不知道大家有沒有覺得枯燥哈不過不要急俗話說理論是實踐的基礎(chǔ)對有了基本的理論認識后我們來看一下下面幾個具體的例子吧下面的幾個例子是我在工作中所遇見的比較常用的的使用場景我精簡了很多有干擾我 接上一小節(jié)徹底征服 Spring AOP 之 理論篇 Spring AOP 實戰(zhàn) 看了上面這么多的理論知識, 不知道大家有沒有覺得枯燥哈. 不過不...
摘要:前提通過前面兩篇文章可以簡單的了解和安裝,今天就將和整合起來使用。然后我運行之前的整合項目,查看監(jiān)控信息如下總結(jié)整篇文章講述了與整合和監(jiān)控平臺的搭建。 showImg(https://segmentfault.com/img/remote/1460000013232432?w=1920&h=1277); 前提 通過前面兩篇文章可以簡單的了解 RocketMQ 和 安裝 RocketMQ...
摘要:參考創(chuàng)建所有運行監(jiān)聽器并發(fā)布應用啟動事件來看下創(chuàng)建運行監(jiān)聽器相關(guān)的源碼創(chuàng)建邏輯和之前實例化初始化器和監(jiān)聽器的一樣,一樣調(diào)用的是方法來獲取配置的監(jiān)聽器名稱并實例化所有的類。 上篇《Spring Boot 2.x 啟動全過程源碼分析(一)入口類剖析》我們分析了 Spring Boot 入口類 SpringApplication 的源碼,并知道了其構(gòu)造原理,這篇我們繼續(xù)往下面分析其核心 ru...
閱讀 2654·2021-11-24 09:39
閱讀 1656·2021-11-24 09:38
閱讀 635·2021-11-22 14:44
閱讀 1893·2021-11-18 10:02
閱讀 2594·2021-11-18 10:02
閱讀 1166·2021-10-14 09:43
閱讀 4254·2021-09-29 09:35
閱讀 533·2021-07-30 15:30