為獲得更好的閱讀體驗,請訪問原文:傳送門
前言: 最近公司來了個大佬,從他那裡學到不少東西,其中一個就是計時
的新姿勢「StopWatch」,趕緊來一起了解了解吧!
一、最簡單的計時
在我們的程式中不免需要對某一個運算或者方法進行計時,以便我們來觀察該運算或方法是否符合我們的預期,所以在我們剛開始接觸 Java 的時候都能寫出類似下面這樣的程式碼來計時:
public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
doSomeThing();
Long endTime = System.currentTimeMillis();
Long elapsedTime = (endTime - startTime) / 1000;
System.out.println("總共耗時:" + elapsedTime + "s");
}
// 用於模擬一些操作
private static void doSomeThing() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
事實上這樣也並沒有什麼問題,並且也能夠執行的很好,但是有一點不太好的就是,自己關注了太多輸出的資訊,下面我們來認識一種更優雅的一種計時方式;
二、StopWatch 類
想要使用它,首先你需要在你的 Maven 中引入 Spring 核心包,當然 Spring MVC 和 Spring Boot 都已經自動引入了該包:
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
現在我們計時的姿勢或許就會變成以下這樣:
public static void main(String[] args) {
StopWatch clock = new StopWatch();
clock.start("開始任務一");
doSomeThing();
clock.stop();
clock.start("開始任務二");
doSomeThing();
clock.stop();
System.out.println(clock.prettyPrint());
}
// 用於模擬一些操作
private static void doSomeThing() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
在最後我們使用 StopWatch
類自帶的 prettyPrint()
方法類格式化我們的輸出,執行程式你會發現你的程式輸出了這樣的東西:
StopWatch '': running time (millis) = 2009
-----------------------------------------
ms % Task name
-----------------------------------------
01005 050% 開始任務一
01004 050% 開始任務二
不僅有總用時,還有每個任務分別的佔用時間和佔用時間的百分比,這或許就會比我們自己輸出要優雅那麼一些;
StopWatch 類是怎麼實現的呢?
當你戳開 StopWatch
的原始碼,你會在總共不到 200 行的程式碼裡看到熟悉的東西:
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
}
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
} else {
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
}
你會發現該類使用 LinkedList
實現了一個叫做 taskList
的佇列,然後每一次開始同樣也是使用 System.currentTimeMillis()
方法來獲取時間,每次除了計算耗時也會構建一個描述當前任務的 TaskInfo
物件,並把它放入 taskList
佇列中。
當執行 prettyPrint()
方法的時候,就從 taskList
佇列中依次取出任務,然後做些格式化的操作:
public String shortSummary() {
return "StopWatch '" + this.getId() + "': running time (millis) = " + this.getTotalTimeMillis();
}
public String prettyPrint() {
StringBuilder sb = new StringBuilder(this.shortSummary());
sb.append('\n');
if (!this.keepTaskList) {
sb.append("No task info kept");
} else {
sb.append("-----------------------------------------\n");
sb.append("ms % Task name\n");
sb.append("-----------------------------------------\n");
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumIntegerDigits(5);
nf.setGroupingUsed(false);
NumberFormat pf = NumberFormat.getPercentInstance();
pf.setMinimumIntegerDigits(3);
pf.setGroupingUsed(false);
StopWatch.TaskInfo[] var4 = this.getTaskInfo();
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
StopWatch.TaskInfo task = var4[var6];
sb.append(nf.format(task.getTimeMillis())).append(" ");
sb.append(pf.format(task.getTimeSeconds() / this.getTotalTimeSeconds())).append(" ");
sb.append(task.getTaskName()).append("\n");
}
}
return sb.toString();
}
摁,新姿勢 get √。
按照慣例黏一個尾巴:
歡迎轉載,轉載請註明出處!
獨立域名部落格:wmyskxz.com
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關注公眾微訊號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693