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