stopwatch java_Java计时器StopWatch实现方法代码实例

本文介绍了Java中StopWatch的三种实现方式:Spring提供的StopWatch、自定义StopWatch以及Scala函数式实现。通过示例代码展示了如何使用这些计时器进行任务计时,并提供了测试代码和输出结果,帮助读者理解StopWatch的工作原理和使用场景。
摘要由CSDN通过智能技术生成

下面提供三种计时器的写法供大家参考,大家可以自行选择自己钟爱的使用。

写法一(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.

*

*

Conceals use of {@code System.currentTimeMillis()}, improving the

* readability of application code and reducing the likelihood of calculation errors.

*

*

Note that this object is not designed to be thread-safe and does not

* use synchronization.

*

*

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 taskList = new LinkedList();

/** 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('\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);

for (TaskInfo task : getTaskInfo()) {

sb.append(nf.format(task.getTimeMillis())).append(" ");

sb.append(pf.format(task.getTimeSeconds() / getTotalTimeSeconds())).append(" ");

sb.append(task.getTaskName()).append("\n");

}

}

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;

/**

*

* StopWatch provides a convenient API for timings.

*

*

*

* To start the watch, call {@link #start()} or {@link StopWatch#createStarted()}. At this point you can:

*

*

*

{@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.

*

{@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.

*

{@link #stop()} the watch to complete the timing session.

*

*

*

* 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.

*

*

*

* 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.

*

*

*

* 1. split(), suspend(), or stop() cannot be invoked twice

* 2. unsplit() may only be called if the watch has been split()

* 3. resume() may only be called if the watch has been suspend()

* 4. start() cannot be called twice without calling reset()

*

*

*

This class is not thread-safe

*

* @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;

}

};

/**

*

* The method is used to find out if the StopWatch is started. A suspended

* StopWatch is also started watch.

*

* @return boolean

* If the StopWatch is started.

*/

abstract boolean isStarted();

/**

*

* 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.

*

*

* @return boolean

* If the StopWatch is stopped.

*/

abstract boolean isStopped();

/**

*

* This method is used to find out whether the StopWatch is suspended.

*

*

* @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;

/**

*

* Constructor.

*

*/

public StopWatch() {

super();

}

/**

*

* Start the stopwatch.

*

*

*

* This method starts a new timing session, clearing any previous values.

*

*

* @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;

}

/**

*

* Stop the stopwatch.

*

*

*

* This method ends a new timing session, allowing the time to be retrieved.

*

*

* @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;

}

/**

*

* Resets the stopwatch. Stops it if need be.

*

*

*

* This method clears the internal values to allow the object to be reused.

*

*/

public void reset() {

this.runningState = State.UNSTARTED;

this.splitState = SplitState.UNSPLIT;

}

/**

*

* Split the time.

*

*

*

* 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.

*

*

* @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;

}

/**

*

* Remove a split.

*

*

*

* This method clears the stop time. The start time is unaffected, enabling timing from the original start point to

* continue.

*

*

* @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;

}

/**

*

* Suspend the stopwatch for later resumption.

*

*

*

* 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.

*

*

* @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;

}

/**

*

* Resume the stopwatch after a suspend.

*

*

*

* 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.

*

*

* @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;

}

/**

*

* Get the time on the stopwatch.

*

*

*

* This is either the time between the start and the moment this method is called, or the amount of time between

* start and stop.

*

*

* @return the time in milliseconds

*/

public long getTime() {

return getNanoTime() / NANO_2_MILLIS;

}

/**

*

* Get the time on the stopwatch in the specified TimeUnit.

*

*

*

* 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}.

*

*

* @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);

}

/**

*

* Get the time on the stopwatch in nanoseconds.

*

*

*

* This is either the time between the start and the moment this method is called, or the amount of time between

* start and stop.

*

*

* @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.");

}

/**

*

* Get the split time on the stopwatch.

*

*

*

* This is the time between start and latest split.

*

*

* @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;

}

/**

*

* Get the split time on the stopwatch in nanoseconds.

*

*

*

* This is the time between start and latest split.

*

*

* @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;

}

/**

*

* Gets a summary of the time that the stopwatch recorded as a string.

*

*

*

* The format used is ISO 8601-like, hours:minutes:seconds.milliseconds.

*

*

* @return the time as a String

*/

@Override

public String toString() {

return DurationFormatUtils.formatDurationHMS(getTime());

}

/**

*

* Gets a summary of the split time that the stopwatch recorded as a string.

*

*

*

* The format used is ISO 8601-like, hours:minutes:seconds.milliseconds.

*

*

* @return the split time as a String

* @since 2.1

*/

public String toSplitString() {

return DurationFormatUtils.formatDurationHMS(getSplitTime());

}

/**

*

* The method is used to find out if the StopWatch is started. A suspended

* StopWatch is also started watch.

*

*

* @return boolean

* If the StopWatch is started.

* @since 3.2

*/

public boolean isStarted() {

return runningState.isStarted();

}

/**

*

* This method is used to find out whether the StopWatch is suspended.

*

*

* @return boolean

* If the StopWatch is suspended.

* @since 3.2

*/

public boolean isSuspended() {

return runningState.isSuspended();

}

/**

*

* 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.

*

*

* @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的简洁及强大了吧!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值