[Tiny] Elapsed Time in Java: currentTimeMillis vs nanoTime

Petr Filaretov - May 25 '23 - - Dev Community

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player