When you need to measure how long some code takes to execute, the first thing that usually comes to mind is currentTimeMillis
:
long start = System.currentTimeMillis();
doSomeWork();
long elapsedMillis = System.currentTimeMillis() - start;
However, there is a potential problem here - elapsedMillis
may become negative in some unfortunate scenarios. Or it could be positive but not correct.
This is because currentTimeMillis
measures wall-clock time, i.e. system clock. And the system clock is always automatically corrected because no computer's clock is perfect. Or you can just change the system date and time manually.
So, if the correction happens during doSomeWork()
, the result of calculating elapsedMillis
will be incorrect.
On the other hand, there is the nanoTime
method which is meant to be exactly for elapsed time measurements. As Javadoc says:
This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.
The usage is the same as with currentTimeMillis
:
long start = System.nanoTime();
doSomeWork();
long elapsedNanos = System.nanoTime() - start;
But wait, you don't have to trust me!
You can test it yourself to make sure that elapsed time can be negative with currentTimeMillis
. Here is the code:
public class ElapsedTimeTest {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Long> elapsedFuture = executor.submit(() -> {
long start;
long elapsed;
while (true) {
start = System.currentTimeMillis();
// do some work
Thread.sleep(1000);
elapsed = System.currentTimeMillis() - start;
if (elapsed < 0) {
return elapsed;
}
if (Thread.currentThread().isInterrupted()) {
return null;
}
}
});
try {
Long elapsed = elapsedFuture.get(5, TimeUnit.MINUTES);
System.out.println("Elapsed time: " + elapsed);
} catch (TimeoutException e) {
System.out.println("No luck so far");
}
executor.shutdownNow();
}
}
Here we submit a calculation of elapsed time in a separate thread and wait for the result for 5 minutes. Just to have enough time to tweak the system clock later.
Elapsed time is calculated with currentTimeMillis
in an infinite loop, and the "work" takes approximately 1 second. (Thread.sleep(1000)
)
The loop will break, and the calculation will be completed if the elapsed time is negative or the thread is interrupted.
Now, run this class, open system clock settings, and update the time, say, by decrementing minutes. You will see that the negative elapsed time is printed, and the program completes.
If you change currentTimeMillis
to nanoTime
and repeat the experiment, nothing happens when you decrement minutes. The program runs until the TimeoutException
on elapsedFuture.get()
.
Dream your code, code your dream.