001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.lang3.time; 019 020/** 021 * <p> 022 * <code>StopWatch</code> provides a convenient API for timings. 023 * </p> 024 * 025 * <p> 026 * To start the watch, call {@link #start()}. At this point you can: 027 * </p> 028 * <ul> 029 * <li>{@link #split()} the watch to get the time whilst the watch continues in the background. {@link #unsplit()} will 030 * remove the effect of the split. At this point, these three options are available again.</li> 031 * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch to continue. Any time between the 032 * suspend and resume will not be counted in the total. At this point, these three options are available again.</li> 033 * <li>{@link #stop()} the watch to complete the timing session.</li> 034 * </ul> 035 * 036 * <p> 037 * It is intended that the output methods {@link #toString()} and {@link #getTime()} should only be called after stop, 038 * split or suspend, however a suitable result will be returned at other points. 039 * </p> 040 * 041 * <p> 042 * NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start, 043 * resume before suspend or unsplit before split. 044 * </p> 045 * 046 * <p> 047 * 1. split(), suspend(), or stop() cannot be invoked twice<br /> 048 * 2. unsplit() may only be called if the watch has been split()<br /> 049 * 3. resume() may only be called if the watch has been suspend()<br /> 050 * 4. start() cannot be called twice without calling reset() 051 * </p> 052 * 053 * <p>This class is not thread-safe</p> 054 * 055 * @since 2.0 056 * @version $Id: StopWatch.java 1552682 2013-12-20 14:19:09Z britter $ 057 */ 058public class StopWatch { 059 060 private static final long NANO_2_MILLIS = 1000000L; 061 062 /** 063 * Enumeration type which indicates the status of stopwatch. 064 */ 065 private enum State { 066 067 UNSTARTED { 068 @Override boolean isStarted() { return false; } 069 @Override boolean isStopped() { return true; } 070 @Override boolean isSuspended() { return false; } 071 }, 072 RUNNING { 073 @Override boolean isStarted() { return true; } 074 @Override boolean isStopped() { return false; } 075 @Override boolean isSuspended() { return false; } 076 }, 077 STOPPED { 078 @Override boolean isStarted() { return false; } 079 @Override boolean isStopped() { return true; } 080 @Override boolean isSuspended() { return false; } 081 }, 082 SUSPENDED { 083 @Override boolean isStarted() { return true; } 084 @Override boolean isStopped() { return false; } 085 @Override boolean isSuspended() { return true; } 086 }; 087 088 /** 089 * <p> 090 * The method is used to find out if the StopWatch is started. A suspended 091 * StopWatch is also started watch. 092 * </p> 093 094 * @param stopWatch 095 * @return boolean 096 * If the StopWatch is started. 097 */ 098 abstract boolean isStarted(); 099 100 /** 101 * <p> 102 * This method is used to find out whether the StopWatch is stopped. The 103 * stopwatch which's not yet started and explicitly stopped stopwatch is 104 * considered as stopped. 105 * </p> 106 * 107 * @param stopWatch 108 * @return boolean 109 * If the StopWatch is stopped. 110 */ 111 abstract boolean isStopped(); 112 113 /** 114 * <p> 115 * This method is used to find out whether the StopWatch is suspended. 116 * </p> 117 * 118 * @param stopWatch 119 * @return boolean 120 * If the StopWatch is suspended. 121 */ 122 abstract boolean isSuspended(); 123 } 124 125 /** 126 * Enumeration type which indicates the split status of stopwatch. 127 */ 128 private enum SplitState { 129 SPLIT, 130 UNSPLIT 131 } 132 /** 133 * The current running state of the StopWatch. 134 */ 135 private State runningState = State.UNSTARTED; 136 137 /** 138 * Whether the stopwatch has a split time recorded. 139 */ 140 private SplitState splitState = SplitState.UNSPLIT; 141 142 /** 143 * The start time. 144 */ 145 private long startTime; 146 147 /** 148 * The start time in Millis - nanoTime is only for elapsed time so we 149 * need to also store the currentTimeMillis to maintain the old 150 * getStartTime API. 151 */ 152 private long startTimeMillis; 153 154 /** 155 * The stop time. 156 */ 157 private long stopTime; 158 159 /** 160 * <p> 161 * Constructor. 162 * </p> 163 */ 164 public StopWatch() { 165 super(); 166 } 167 168 /** 169 * <p> 170 * Start the stopwatch. 171 * </p> 172 * 173 * <p> 174 * This method starts a new timing session, clearing any previous values. 175 * </p> 176 * 177 * @throws IllegalStateException 178 * if the StopWatch is already running. 179 */ 180 public void start() { 181 if (this.runningState == State.STOPPED) { 182 throw new IllegalStateException("Stopwatch must be reset before being restarted. "); 183 } 184 if (this.runningState != State.UNSTARTED) { 185 throw new IllegalStateException("Stopwatch already started. "); 186 } 187 this.startTime = System.nanoTime(); 188 this.startTimeMillis = System.currentTimeMillis(); 189 this.runningState = State.RUNNING; 190 } 191 192 193 /** 194 * <p> 195 * Stop the stopwatch. 196 * </p> 197 * 198 * <p> 199 * This method ends a new timing session, allowing the time to be retrieved. 200 * </p> 201 * 202 * @throws IllegalStateException 203 * if the StopWatch is not running. 204 */ 205 public void stop() { 206 if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) { 207 throw new IllegalStateException("Stopwatch is not running. "); 208 } 209 if (this.runningState == State.RUNNING) { 210 this.stopTime = System.nanoTime(); 211 } 212 this.runningState = State.STOPPED; 213 } 214 215 /** 216 * <p> 217 * Resets the stopwatch. Stops it if need be. 218 * </p> 219 * 220 * <p> 221 * This method clears the internal values to allow the object to be reused. 222 * </p> 223 */ 224 public void reset() { 225 this.runningState = State.UNSTARTED; 226 this.splitState = SplitState.UNSPLIT; 227 } 228 229 /** 230 * <p> 231 * Split the time. 232 * </p> 233 * 234 * <p> 235 * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected, 236 * enabling {@link #unsplit()} to continue the timing from the original start point. 237 * </p> 238 * 239 * @throws IllegalStateException 240 * if the StopWatch is not running. 241 */ 242 public void split() { 243 if (this.runningState != State.RUNNING) { 244 throw new IllegalStateException("Stopwatch is not running. "); 245 } 246 this.stopTime = System.nanoTime(); 247 this.splitState = SplitState.SPLIT; 248 } 249 250 /** 251 * <p> 252 * Remove a split. 253 * </p> 254 * 255 * <p> 256 * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to 257 * continue. 258 * </p> 259 * 260 * @throws IllegalStateException 261 * if the StopWatch has not been split. 262 */ 263 public void unsplit() { 264 if (this.splitState != SplitState.SPLIT) { 265 throw new IllegalStateException("Stopwatch has not been split. "); 266 } 267 this.splitState = SplitState.UNSPLIT; 268 } 269 270 /** 271 * <p> 272 * Suspend the stopwatch for later resumption. 273 * </p> 274 * 275 * <p> 276 * This method suspends the watch until it is resumed. The watch will not include time between the suspend and 277 * resume calls in the total time. 278 * </p> 279 * 280 * @throws IllegalStateException 281 * if the StopWatch is not currently running. 282 */ 283 public void suspend() { 284 if (this.runningState != State.RUNNING) { 285 throw new IllegalStateException("Stopwatch must be running to suspend. "); 286 } 287 this.stopTime = System.nanoTime(); 288 this.runningState = State.SUSPENDED; 289 } 290 291 /** 292 * <p> 293 * Resume the stopwatch after a suspend. 294 * </p> 295 * 296 * <p> 297 * This method resumes the watch after it was suspended. The watch will not include time between the suspend and 298 * resume calls in the total time. 299 * </p> 300 * 301 * @throws IllegalStateException 302 * if the StopWatch has not been suspended. 303 */ 304 public void resume() { 305 if (this.runningState != State.SUSPENDED) { 306 throw new IllegalStateException("Stopwatch must be suspended to resume. "); 307 } 308 this.startTime += System.nanoTime() - this.stopTime; 309 this.runningState = State.RUNNING; 310 } 311 312 /** 313 * <p> 314 * Get the time on the stopwatch. 315 * </p> 316 * 317 * <p> 318 * This is either the time between the start and the moment this method is called, or the amount of time between 319 * start and stop. 320 * </p> 321 * 322 * @return the time in milliseconds 323 */ 324 public long getTime() { 325 return getNanoTime() / NANO_2_MILLIS; 326 } 327 /** 328 * <p> 329 * Get the time on the stopwatch in nanoseconds. 330 * </p> 331 * 332 * <p> 333 * This is either the time between the start and the moment this method is called, or the amount of time between 334 * start and stop. 335 * </p> 336 * 337 * @return the time in nanoseconds 338 * @since 3.0 339 */ 340 public long getNanoTime() { 341 if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) { 342 return this.stopTime - this.startTime; 343 } else if (this.runningState == State.UNSTARTED) { 344 return 0; 345 } else if (this.runningState == State.RUNNING) { 346 return System.nanoTime() - this.startTime; 347 } 348 throw new RuntimeException("Illegal running state has occurred."); 349 } 350 351 /** 352 * <p> 353 * Get the split time on the stopwatch. 354 * </p> 355 * 356 * <p> 357 * This is the time between start and latest split. 358 * </p> 359 * 360 * @return the split time in milliseconds 361 * 362 * @throws IllegalStateException 363 * if the StopWatch has not yet been split. 364 * @since 2.1 365 */ 366 public long getSplitTime() { 367 return getSplitNanoTime() / NANO_2_MILLIS; 368 } 369 /** 370 * <p> 371 * Get the split time on the stopwatch in nanoseconds. 372 * </p> 373 * 374 * <p> 375 * This is the time between start and latest split. 376 * </p> 377 * 378 * @return the split time in nanoseconds 379 * 380 * @throws IllegalStateException 381 * if the StopWatch has not yet been split. 382 * @since 3.0 383 */ 384 public long getSplitNanoTime() { 385 if (this.splitState != SplitState.SPLIT) { 386 throw new IllegalStateException("Stopwatch must be split to get the split time. "); 387 } 388 return this.stopTime - this.startTime; 389 } 390 391 /** 392 * Returns the time this stopwatch was started. 393 * 394 * @return the time this stopwatch was started 395 * @throws IllegalStateException 396 * if this StopWatch has not been started 397 * @since 2.4 398 */ 399 public long getStartTime() { 400 if (this.runningState == State.UNSTARTED) { 401 throw new IllegalStateException("Stopwatch has not been started"); 402 } 403 // System.nanoTime is for elapsed time 404 return this.startTimeMillis; 405 } 406 407 /** 408 * <p> 409 * Gets a summary of the time that the stopwatch recorded as a string. 410 * </p> 411 * 412 * <p> 413 * The format used is ISO8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>. 414 * </p> 415 * 416 * @return the time as a String 417 */ 418 @Override 419 public String toString() { 420 return DurationFormatUtils.formatDurationHMS(getTime()); 421 } 422 423 /** 424 * <p> 425 * Gets a summary of the split time that the stopwatch recorded as a string. 426 * </p> 427 * 428 * <p> 429 * The format used is ISO8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>. 430 * </p> 431 * 432 * @return the split time as a String 433 * @since 2.1 434 */ 435 public String toSplitString() { 436 return DurationFormatUtils.formatDurationHMS(getSplitTime()); 437 } 438 439 /** 440 * <p> 441 * The method is used to find out if the StopWatch is started. A suspended 442 * StopWatch is also started watch. 443 * </p> 444 * 445 * @return boolean 446 * If the StopWatch is started. 447 * @since 3.2 448 */ 449 public boolean isStarted() { 450 return runningState.isStarted(); 451 } 452 453 /** 454 * <p> 455 * This method is used to find out whether the StopWatch is suspended. 456 * </p> 457 * 458 * @return boolean 459 * If the StopWatch is suspended. 460 * @since 3.2 461 */ 462 public boolean isSuspended() { 463 return runningState.isSuspended(); 464 } 465 466 /** 467 * <p> 468 * This method is used to find out whether the StopWatch is stopped. The 469 * stopwatch which's not yet started and explicitly stopped stopwatch is 470 * considered as stopped. 471 * </p> 472 * 473 * @return boolean 474 * If the StopWatch is stopped. 475 * @since 3.2 476 */ 477 public boolean isStopped() { 478 return runningState.isStopped(); 479 } 480 481}