Many things in programming sound like they should be easy, but are quite hard. Calculating elapsed time in Java is one of those. How hard could that be? As it turns out, it can be tricky.
For starters, we have the fact that time itself is a tricky concept. For a quick example, remember that many places around the world observe Daylight Savings Time. Failing to take DST into account when you should, can and often does result in incorrect calculations.
Then, we have the problem that, in Java, there are many ways of performing this calculation. You have different alternatives brought to you by the Java API itself—and don’t forget that Java 8 brought us an entirely new date and time API, based on the popular Joda-time open-source project. Additionally, third-party libraries add their own options to the mix.
In this post, we’ll offer some guidance so you can decide the most appropriate option to use when calculating elapsed time in Java. We’ll start by covering the different types of elapsed time calculation one might need to perform. Then we’ll quickly turn our focus to the alternatives themselves, explaining the strengths and weaknesses of each one. By the end of the post, you’ll be aware of the main ways in which you can perform this calculation, and you’ll be able to make an informed decision about the best alternative for your scenario. Let’s begin!
Java Elapsed Time Calculation: Not a Single Thing
There are several different ways of calculating elapsed time in Java. To make the right call for your scenario, there are several factors you must consider. One of them refers to the type of elapsed time calculation.
When it comes to the motivations behind calculating elapsed time, we can broadly categorize them into two main groups. The first category is what I call the “business logic” motivation. This is when you have to calculate the elapsed time of some event that is relevant to your application’s core business or domain. A good example would be recording the time a worker clocks in and out and then calculating the amount of time worked.
The second category is what I like to call “meta” motivation. This means calculating elapsed time for some reason that’s not related to the domain of your application, but rather, refers to some concern about the development itself. The classic example here would be benchmarking a function for performance measurement purposes.
Why is this distinction important? For now, understand that it’s going to be crucial to decide which one of several options is the right one to calculate elapsed time. We’ll cover that in more detail in the sections to come.
Calculating Elapsed Time in Java in All Shapes and Sizes
Without further ado, let’s now cover some of the main ways to perform a calculation of elapsed time in Java.
The Quick, Easy, and Often Incorrect Way: System.currentTimeMillis()
We start our list of ways to measure elapsed time in Java with a solution that’s easy but often incorrect. First, we’ll show a quick example of how to perform the calculation, and after that, we’ll explain the code:
long start = System.currentTimeMillis(); // some time passes long end = System.currentTimeMillis(); long elapsedTime = end - start;
In the example above, we’re using the “System.currentTimeMillis()” static method. The method returns a long value, which refers to the number of milliseconds since January 1st, 1970, in UTC. So, why is this solution problematic?
The results you get from the difference may be inaccurate because the method measures what we call “wall-clock time.” That means it can change for a variety of reasons, including changes in the system clock or even leap-seconds.
So, in short, you should avoid using the currentTimeMillis() method for calculating elapsed time if you need high precision.
The Also Quick, Also Easy and More Likely to Be Correct Way: System.nanoTime()
Let’s now see another way of calculating elapsed time. We’ll first notice the example and then comment on it.
long start = System.nanoTime(); // some time passes long end = System.nanoTime(); long elapsedTime = end - start;
As you can see, the code looks a lot like the code in the previous example. The only difference is that now we’re using the “nanoTime()” method instead of “currentTimeMillis().” So, what’s the difference between the two methods?
The first notable difference is that “nanoTime(),” as its name suggests, returns its result in nanoseconds. Per the documentation, we can see that the source of its return value is the high-resolution time source of the JVM (Java Virtual Machine.)
The documentation also stresses some other important facts. The first of them is that you can only use this method to calculate elapsed time. In other words, the value returned by the method isn’t meaningful, since it has no relation to any time system.
Another important thing to bear in mind when using this method is that, while it provides nanosecond precision, it doesn’t necessarily offer nanosecond resolution. “Resolution” here means the frequency with which the value is updated.
Finally, bear in mind that the method isn’t thread-safe.
The StopWatch Class
We’ll now turn our focus to libraries, by covering the StopWatch class provided by the Apache Commons Lang API. Let’s see a quick example:
StopWatch watch = new StopWatch(); watch.start(); // call to the methods you want to benchmark watch.stop(); long result = watch.getTime();
As you can see from the code above, the usage of the StopWatch is very simple. You start the timing by calling the “start()” method. Then you do whatever tasks you need to perform. After that, you’re ready to call “stop()” and then “getTime()” which will return the elapsed time in milliseconds. Keep in mind that this class presents temporal coupling. That means you can’t, for instance, stop a clock that you haven’t started or started a clock that is already running. Attempting to do so will result in an exception being thrown.
Post-Java 8 Alternatives
Java 8 brought us the new java. time API, which introduced several new types representing important time concepts. You’ll now see how to calculate elapsed time using two of the new java.time types: the Instant and the Duration classes. Take a look at the following example:
Instant start = Instant.now(); // time passes Instant end = Instant.now(); Duration timeElapsed = Duration.between(start, end);
The first new thing we notice is the Instant class, which is immutable and thread-safe. This new type represents a single, instantaneous point in the timeline, and, as such, you can use it to calculate elapsed time. But to do that, we’d need a new class yet, and that’s where Duration comes in to help.
Duration, like Instant, is an immutable and thread-safe class. According to the documentation, it represents an amount of time that is time-based, such as “4 hours” or “36 seconds.”
By calling the “between” method and providing it two instances of the Instant class, we’re able to calculate a duration. Afterwards, you can use one of the Duration’s methods—such as “today's(),” “toHours(),” “toMinutes(),” and so on—to convert the duration to the unit that’s more appropriate for your needs.
Learn About Time. It’s About Time!
In this post, we’ve covered some of the main alternatives for calculating elapsed time in Java. As you can see, some alternatives cater to different needs. If you need the highest possible precision, you won’t be using “currentTimeMillis(),” for instance. On the other hand, if you need thread-safety, you can discard “nanoTime().”
Also, keep in mind that we haven’t presented all of the possible alternatives to perform this calculation. For instance, we didn’t mention this other StopWatch class that is provided by the Guava API.
No comments:
Post a Comment