歡迎您光臨本站 註冊首頁

Java計時器StopWatch實現方法代碼實例

←手機掃碼閱讀     f2h0b53ohn @ 2020-07-07 , reply:0

下面提供三種計時器的寫法供大家參考,大家可以自行選擇自己鍾愛的使用。

寫法一(Spring 包提供的計時器):

  import java.text.NumberFormat;  import java.util.LinkedList;  import java.util.List;    /**   * Simple stop watch, allowing for timing of a number of tasks,   * exposing total running time and running time for each named task.   *   * <p>Conceals use of {@code System.currentTimeMillis()}, improving the   * readability of application code and reducing the likelihood of calculation errors.   *   * <p>Note that this object is not designed to be thread-safe and does not   * use synchronization.   *   * <p>This class is normally used to verify performance during proof-of-concepts   * and in development, rather than as part of production applications.   *   * @author Rod Johnson   * @author Juergen Hoeller   * @author Sam Brannen   * @since May 2, 2001   */  public class StopWatch {      /**     * Identifier of this stop watch.     * Handy when we have output from multiple stop watches     * and need to distinguish between them in log or console output.     */    private final String id;      private boolean keepTaskList = true;      private final List<TaskInfo> taskList = new LinkedList<TaskInfo>();      /** Start time of the current task */    private long startTimeMillis;      /** Is the stop watch currently running? */    private boolean running;      /** Name of the current task */    private String currentTaskName;      private TaskInfo lastTaskInfo;      private int taskCount;      /** Total running time */    private long totalTimeMillis;        /**     * Construct a new stop watch. Does not start any task.     */    public StopWatch() {      this("");    }      /**     * Construct a new stop watch with the given id.     * Does not start any task.     * @param id identifier for this stop watch.     * Handy when we have output from multiple stop watches     * and need to distinguish between them.     */    public StopWatch(String id) {      this.id = id;    }        /**     * Return the id of this stop watch, as specified on construction.     * @return the id (empty String by default)     * @since 4.2.2     * @see #StopWatch(String)     */    public String getId() {      return this.id;    }      /**     * Determine whether the TaskInfo array is built over time. Set this to     * "false" when using a StopWatch for millions of intervals, or the task     * info structure will consume excessive memory. Default is "true".     */    public void setKeepTaskList(boolean keepTaskList) {      this.keepTaskList = keepTaskList;    }        /**     * Start an unnamed task. The results are undefined if {@link #stop()}     * or timing methods are called without invoking this method.     * @see #stop()     */    public void start() throws IllegalStateException {      start("");    }      /**     * Start a named task. The results are undefined if {@link #stop()}     * or timing methods are called without invoking this method.     * @param taskName the name of the task to start     * @see #stop()     */    public void start(String taskName) throws IllegalStateException {      if (this.running) {        throw new IllegalStateException("Can't start StopWatch: it's already running");      }      this.running = true;      this.currentTaskName = taskName;      this.startTimeMillis = System.currentTimeMillis();    }      /**     * Stop the current task. The results are undefined if timing     * methods are called without invoking at least one pair     * {@code start()} / {@code stop()} methods.     * @see #start()     */    public void stop() throws IllegalStateException {      if (!this.running) {        throw new IllegalStateException("Can't stop StopWatch: it's not running");      }      long lastTime = System.currentTimeMillis() - this.startTimeMillis;      this.totalTimeMillis += lastTime;      this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);      if (this.keepTaskList) {        this.taskList.add(lastTaskInfo);      }      ++this.taskCount;      this.running = false;      this.currentTaskName = null;    }      /**     * Return whether the stop watch is currently running.     * @see #currentTaskName()     */    public boolean isRunning() {      return this.running;    }      /**     * Return the name of the currently running task, if any.     * @since 4.2.2     * @see #isRunning()     */    public String currentTaskName() {      return this.currentTaskName;    }        /**     * Return the time taken by the last task.     */    public long getLastTaskTimeMillis() throws IllegalStateException {      if (this.lastTaskInfo == null) {        throw new IllegalStateException("No tasks run: can't get last task interval");      }      return this.lastTaskInfo.getTimeMillis();    }      /**     * Return the name of the last task.     */    public String getLastTaskName() throws IllegalStateException {      if (this.lastTaskInfo == null) {        throw new IllegalStateException("No tasks run: can't get last task name");      }      return this.lastTaskInfo.getTaskName();    }      /**     * Return the last task as a TaskInfo object.     */    public TaskInfo getLastTaskInfo() throws IllegalStateException {      if (this.lastTaskInfo == null) {        throw new IllegalStateException("No tasks run: can't get last task info");      }      return this.lastTaskInfo;    }        /**     * Return the total time in milliseconds for all tasks.     */    public long getTotalTimeMillis() {      return this.totalTimeMillis;    }      /**     * Return the total time in seconds for all tasks.     */    public double getTotalTimeSeconds() {      return this.totalTimeMillis / 1000.0;    }      /**     * Return the number of tasks timed.     */    public int getTaskCount() {      return this.taskCount;    }      /**     * Return an array of the data for tasks performed.     */    public TaskInfo[] getTaskInfo() {      if (!this.keepTaskList) {        throw new UnsupportedOperationException("Task info is not being kept!");      }      return this.taskList.toArray(new TaskInfo[this.taskList.size()]);    }        /**     * Return a short description of the total running time.     */    public String shortSummary() {      return "StopWatch '" + getId() + "': running time (millis) = " + getTotalTimeMillis();    }      /**     * Return a string with a table describing all tasks performed.     * For custom reporting, call getTaskInfo() and use the task info directly.     */    public String prettyPrint() {      StringBuilder sb = new StringBuilder(shortSummary());      sb.append(' ');      if (!this.keepTaskList) {        sb.append("No task info kept");      }      else {        sb.append("----------------------------------------- ");        sb.append("ms   %   Task name ");        sb.append("----------------------------------------- ");        NumberFormat nf = NumberFormat.getNumberInstance();        nf.setMinimumIntegerDigits(5);        nf.setGroupingUsed(false);        NumberFormat pf = NumberFormat.getPercentInstance();        pf.setMinimumIntegerDigits(3);        pf.setGroupingUsed(false);        for (TaskInfo task : getTaskInfo()) {          sb.append(nf.format(task.getTimeMillis())).append(" ");          sb.append(pf.format(task.getTimeSeconds() / getTotalTimeSeconds())).append(" ");          sb.append(task.getTaskName()).append(" ");        }      }      return sb.toString();    }      /**     * Return an informative string describing all tasks performed     * For custom reporting, call {@code getTaskInfo()} and use the task info directly.     */    @Override    public String toString() {      StringBuilder sb = new StringBuilder(shortSummary());      if (this.keepTaskList) {        for (TaskInfo task : getTaskInfo()) {          sb.append("; [").append(task.getTaskName()).append("] took ").append(task.getTimeMillis());          long percent = Math.round((100.0 * task.getTimeSeconds()) / getTotalTimeSeconds());          sb.append(" = ").append(percent).append("%");        }      }      else {        sb.append("; no task info kept");      }      return sb.toString();    }        /**     * Inner class to hold data about one task executed within the stop watch.     */    public static final class TaskInfo {        private final String taskName;        private final long timeMillis;        TaskInfo(String taskName, long timeMillis) {        this.taskName = taskName;        this.timeMillis = timeMillis;      }        /**       * Return the name of this task.       */      public String getTaskName() {        return this.taskName;      }        /**       * Return the time in milliseconds this task took.       */      public long getTimeMillis() {        return this.timeMillis;      }        /**       * Return the time in seconds this task took.       */      public double getTimeSeconds() {        return (this.timeMillis / 1000.0);      }    }    }

 

下面寫一個調用:

  public static void main(String[] args) throws InterruptedException {  //    StopWatchTest.test0();     StopWatchTest.test1();  }    public static void test1() throws InterruptedException {     StopWatch sw = new StopWatch("test");     sw.start("task1");     // do something    Thread.sleep(100);    sw.stop();    sw.start("task2");    // do something    Thread.sleep(200);    sw.stop();    System.out.println("sw.prettyPrint()~~~~~~~~~~~~~~~~~");    System.out.println(sw.prettyPrint());  }

 

運行結果:
 

sw.prettyPrint()~~~~~~~~~~~~~~~~~
 StopWatch 'test': running time (millis) = 308
 -----------------------------------------
 ms % Task name
 -----------------------------------------
 00104 034% task1
 00204 066% task2
 ---------------------

start開始記錄,stop停止記錄,然後通過StopWatch的prettyPrint方法,可直觀的輸出代碼執行耗時,以及執行時間百分比,瞬間感覺比之前的方式高大上了一個檔次。

 除此之外,還有以下兩個方法shortSummary,getTotalTimeMillis,查看程序執行時間。

寫法二(apache.commons實現的計時器):

  import java.util.concurrent.TimeUnit;    /**   * <p>   * <code>StopWatch</code> provides a convenient API for timings.   * </p>   *   * <p>   * To start the watch, call {@link #start()} or {@link StopWatch#createStarted()}. At this point you can:   * </p>   * <ul>   * <li>{@link #split()} the watch to get the time whilst the watch continues in the background. {@link #unsplit()} will   * remove the effect of the split. At this point, these three options are available again.</li>   * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch to continue. Any time between the   * suspend and resume will not be counted in the total. At this point, these three options are available again.</li>   * <li>{@link #stop()} the watch to complete the timing session.</li>   * </ul>   *   * <p>   * It is intended that the output methods {@link #toString()} and {@link #getTime()} should only be called after stop,   * split or suspend, however a suitable result will be returned at other points.   * </p>   *   * <p>   * NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start,   * resume before suspend or unsplit before split.   * </p>   *   * <p>   * 1. split(), suspend(), or stop() cannot be invoked twice<br>   * 2. unsplit() may only be called if the watch has been split()<br>   * 3. resume() may only be called if the watch has been suspend()<br>   * 4. start() cannot be called twice without calling reset()   * </p>   *   * <p>This class is not thread-safe</p>   *   * @since 2.0   */  public class StopWatch {      private static final long NANO_2_MILLIS = 1000000L;        /**     * Provides a started stopwatch for convenience.     *     * @return StopWatch a stopwatch that's already been started.     *     * @since 3.5     */    public static StopWatch createStarted() {      final StopWatch sw = new StopWatch();      sw.start();      return sw;    }      /**     * Enumeration type which indicates the status of stopwatch.     */    private enum State {        UNSTARTED {        @Override        boolean isStarted() {          return false;        }        @Override        boolean isStopped() {          return true;        }        @Override        boolean isSuspended() {          return false;        }      },      RUNNING {        @Override        boolean isStarted() {          return true;        }        @Override        boolean isStopped() {          return false;        }        @Override        boolean isSuspended() {          return false;        }      },      STOPPED {        @Override        boolean isStarted() {          return false;        }        @Override        boolean isStopped() {          return true;        }        @Override        boolean isSuspended() {          return false;        }      },      SUSPENDED {        @Override        boolean isStarted() {          return true;        }        @Override        boolean isStopped() {          return false;        }        @Override        boolean isSuspended() {          return true;        }      };        /**       * <p>       * The method is used to find out if the StopWatch is started. A suspended       * StopWatch is also started watch.       * </p>         * @return boolean       *       If the StopWatch is started.       */      abstract boolean isStarted();        /**       * <p>       * This method is used to find out whether the StopWatch is stopped. The       * stopwatch which's not yet started and explicitly stopped stopwatch is       * considered as stopped.       * </p>       *       * @return boolean       *       If the StopWatch is stopped.       */      abstract boolean isStopped();        /**       * <p>       * This method is used to find out whether the StopWatch is suspended.       * </p>       *       * @return boolean       *       If the StopWatch is suspended.       */      abstract boolean isSuspended();    }      /**     * Enumeration type which indicates the split status of stopwatch.     */    private enum SplitState {      SPLIT,      UNSPLIT    }    /**     * The current running state of the StopWatch.     */    private State runningState = State.UNSTARTED;      /**     * Whether the stopwatch has a split time recorded.     */    private SplitState splitState = SplitState.UNSPLIT;      /**     * The start time.     */    private long startTime;      /**     * The start time in Millis - nanoTime is only for elapsed time so we     * need to also store the currentTimeMillis to maintain the old     * getStartTime API.     */    private long startTimeMillis;      /**     * The stop time.     */    private long stopTime;      /**     * <p>     * Constructor.     * </p>     */    public StopWatch() {      super();    }      /**     * <p>     * Start the stopwatch.     * </p>     *     * <p>     * This method starts a new timing session, clearing any previous values.     * </p>     *     * @throws IllegalStateException     *       if the StopWatch is already running.     */    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;    }        /**     * <p>     * Stop the stopwatch.     * </p>     *     * <p>     * This method ends a new timing session, allowing the time to be retrieved.     * </p>     *     * @throws IllegalStateException     *       if the StopWatch is not running.     */    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;    }      /**     * <p>     * Resets the stopwatch. Stops it if need be.     * </p>     *     * <p>     * This method clears the internal values to allow the object to be reused.     * </p>     */    public void reset() {      this.runningState = State.UNSTARTED;      this.splitState = SplitState.UNSPLIT;    }      /**     * <p>     * Split the time.     * </p>     *     * <p>     * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected,     * enabling {@link #unsplit()} to continue the timing from the original start point.     * </p>     *     * @throws IllegalStateException     *       if the StopWatch is not running.     */    public void split() {      if (this.runningState != State.RUNNING) {        throw new IllegalStateException("Stopwatch is not running. ");      }      this.stopTime = System.nanoTime();      this.splitState = SplitState.SPLIT;    }      /**     * <p>     * Remove a split.     * </p>     *     * <p>     * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to     * continue.     * </p>     *     * @throws IllegalStateException     *       if the StopWatch has not been split.     */    public void unsplit() {      if (this.splitState != SplitState.SPLIT) {        throw new IllegalStateException("Stopwatch has not been split. ");      }      this.splitState = SplitState.UNSPLIT;    }      /**     * <p>     * Suspend the stopwatch for later resumption.     * </p>     *     * <p>     * This method suspends the watch until it is resumed. The watch will not include time between the suspend and     * resume calls in the total time.     * </p>     *     * @throws IllegalStateException     *       if the StopWatch is not currently running.     */    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;    }      /**     * <p>     * Resume the stopwatch after a suspend.     * </p>     *     * <p>     * This method resumes the watch after it was suspended. The watch will not include time between the suspend and     * resume calls in the total time.     * </p>     *     * @throws IllegalStateException     *       if the StopWatch has not been suspended.     */    public void resume() {      if (this.runningState != State.SUSPENDED) {        throw new IllegalStateException("Stopwatch must be suspended to resume. ");      }      this.startTime += System.nanoTime() - this.stopTime;      this.runningState = State.RUNNING;    }      /**     * <p>     * Get the time on the stopwatch.     * </p>     *     * <p>     * This is either the time between the start and the moment this method is called, or the amount of time between     * start and stop.     * </p>     *     * @return the time in milliseconds     */    public long getTime() {      return getNanoTime() / NANO_2_MILLIS;    }      /**     * <p>     * Get the time on the stopwatch in the specified TimeUnit.     * </p>     *     * <p>     * This is either the time between the start and the moment this method is called, or the amount of time between     * start and stop. The resulting time will be expressed in the desired TimeUnit with any remainder rounded down.     * For example, if the specified unit is {@code TimeUnit.HOURS} and the stopwatch time is 59 minutes, then the     * result returned will be {@code 0}.     * </p>     *     * @param timeUnit the unit of time, not null     * @return the time in the specified TimeUnit, rounded down     * @since 3.5     */    public long getTime(final TimeUnit timeUnit) {      return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);    }      /**     * <p>     * Get the time on the stopwatch in nanoseconds.     * </p>     *     * <p>     * This is either the time between the start and the moment this method is called, or the amount of time between     * start and stop.     * </p>     *     * @return the time in nanoseconds     * @since 3.0     */    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.");    }      /**     * <p>     * Get the split time on the stopwatch.     * </p>     *     * <p>     * This is the time between start and latest split.     * </p>     *     * @return the split time in milliseconds     *     * @throws IllegalStateException     *       if the StopWatch has not yet been split.     * @since 2.1     */    public long getSplitTime() {      return getSplitNanoTime() / NANO_2_MILLIS;    }    /**     * <p>     * Get the split time on the stopwatch in nanoseconds.     * </p>     *     * <p>     * This is the time between start and latest split.     * </p>     *     * @return the split time in nanoseconds     *     * @throws IllegalStateException     *       if the StopWatch has not yet been split.     * @since 3.0     */    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;    }      /**     * Returns the time this stopwatch was started.     *     * @return the time this stopwatch was started     * @throws IllegalStateException     *       if this StopWatch has not been started     * @since 2.4     */    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;    }      /**     * <p>     * Gets a summary of the time that the stopwatch recorded as a string.     * </p>     *     * <p>     * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.     * </p>     *     * @return the time as a String     */    @Override    public String toString() {      return DurationFormatUtils.formatDurationHMS(getTime());    }      /**     * <p>     * Gets a summary of the split time that the stopwatch recorded as a string.     * </p>     *     * <p>     * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.     * </p>     *     * @return the split time as a String     * @since 2.1     */    public String toSplitString() {      return DurationFormatUtils.formatDurationHMS(getSplitTime());    }      /**     * <p>     * The method is used to find out if the StopWatch is started. A suspended     * StopWatch is also started watch.     * </p>     *     * @return boolean     *       If the StopWatch is started.     * @since 3.2     */    public boolean isStarted() {      return runningState.isStarted();    }      /**     * <p>     * This method is used to find out whether the StopWatch is suspended.     * </p>     *     * @return boolean     *       If the StopWatch is suspended.     * @since 3.2     */    public boolean isSuspended() {      return runningState.isSuspended();    }      /**     * <p>     * This method is used to find out whether the StopWatch is stopped. The     * stopwatch which's not yet started and explicitly stopped stopwatch is     * considered as stopped.     * </p>     *     * @return boolean     *       If the StopWatch is stopped.     * @since 3.2     */    public boolean isStopped() {      return runningState.isStopped();    }    }

 

寫法三(Scala函數寫法):

  import org.slf4j.LoggerFactory    /**   * 類功能描述:Debug日誌追蹤   *   * @author barry create at 18-8-29 下午3:41   * @version 1.0.0   */  object Debug {   val LOGGER = LoggerFactory.getLogger(getClass)   val counter = collection.mutable.Map[String, Int]() // label -> count   val times = collection.mutable.Map[String, Long]() // label - time(ns)     /**    *追蹤代碼塊    * @param label 標籤名    * @param codeBlock 代碼塊    * @tparam T 返回結果類型    * @return    */   def trace[T](label: String)(codeBlock: => T) = {    val t0 = System.nanoTime()    val result = codeBlock    val t1 = System.nanoTime()    counter.get(label).map(_counter => counter.put(label, _counter + 1)).orElse(counter.put(label, 1))    times.get(label).map(cost => times.put(label, cost + (t1 - t0))).orElse(times.put(label, t1 - t0))    result   }     /**    * 打印日誌    */   def info(): Unit = {    LOGGER.warn("FinallyDone...")    LOGGER.warn(s"counter:${counter}")    LOGGER.warn(s"times:${times.map { case (label, cost) => (label, cost / 1000000)}}ms")   }     /**    * 重新計數    */   def reset(): Unit = {    counter.clear()    times.clear()   }  }

 

參考下面測試代碼:

java版本:

  /**   * @author WangXueXing create at 19-7-31 下午1:46   * @version 1.0.0   */  public class StopWatchDemo {    public static void main(String[] args){      Debug.trace("方法1調用次數及用時", ()->{        try {          Thread.sleep(2000);        } catch (InterruptedException e) {          e.printStackTrace();        }        return "";      });        Debug.trace("方法1調用次數及用時", ()->{        try {          Thread.sleep(2000);        } catch (InterruptedException e) {          e.printStackTrace();        }        return "";      });        Debug.trace("方法2調用次數及用時", ()->{        try {          Thread.sleep(2000);        } catch (InterruptedException e) {          e.printStackTrace();        }        return 10;      });        Debug.info();    }  }

 

輸出結果:

15:29:32.228 [main] WARN test.Debug$ - FinallyDone...
 15:29:32.361 [main] WARN test.Debug$ - counter:Map(方法2調用次數及用時 -> 1, 方法1調用次數及用時 -> 2)
 15:29:32.364 [main] WARN test.Debug$ - times:Map(方法2調用次數及用時 -> 2000, 方法1調用次數及用時 -> 4000)ms
 

scala版本:

  /**   * @author WangXueXing create at 19-8-1 下午3:40   * @version 1.0.0   */  object StopWatchTest {   def main(args: Array[String]): Unit = {    Debug.trace("方法1調用次數及用時")( Thread.sleep(200))    Debug.trace("方法1調用次數及用時")( Thread.sleep(200))    Debug.trace("方法2調用次數及用時")( Thread.sleep(200))    Debug.info()   }  }

 

輸出結果:

15:43:58.601 [main] WARN test.stopwatch.Debug$ - FinallyDone...
 15:43:58.735 [main] WARN test.stopwatch.Debug$ - counter:Map(方法2調用次數及用時 -> 1, 方法1調用次數及用時 -> 2)
 15:43:58.739 [main] WARN test.stopwatch.Debug$ - times:Map(方法2調用次數及用時 -> 200, 方法1調用次數及用時 -> 400)ms
 

對比java版本與scala版本,是不是看到scala的簡潔及強大了吧!


[f2h0b53ohn ] Java計時器StopWatch實現方法代碼實例已經有495次圍觀

http://coctec.com/docs/java/show-post-241604.html