Java計時新姿勢

我沒有三顆心臟發表於2019-07-30

為獲得更好的閱讀體驗,請訪問原文:傳送門
前言: 最近公司來了個大佬,從他那裡學到不少東西,其中一個就是計時
的新姿勢「StopWatch」,趕緊來一起了解了解吧!

Java計時新姿勢

一、最簡單的計時


在我們的程式中不免需要對某一個運算或者方法進行計時,以便我們來觀察該運算或方法是否符合我們的預期,所以在我們剛開始接觸 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

相關文章