Measure Elapsed Time in Java – 在Java中测量耗时

最后修改: 2018年 4月 22日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’re going to have a look at how to measure elapsed time in Java. While this may sound easy, there’re a few pitfalls that we must be aware of.

在这篇文章中,我们将看看如何在Java中测量过往的时间。虽然这听起来很容易,但有几个陷阱是我们必须注意的。

We’ll explore standard Java classes and external packages that provide functionality to measure elapsed time.

我们将探索标准的Java类和外部包,它们提供了测量耗时的功能。

2. Simple Measurements

2.简单的测量

2.1. currentTimeMillis()

2.1.currentTimeMillis()

When we encounter a requirement to measure elapsed time in Java, we may try to do it like:

当我们遇到在Java中测量经过的时间的要求时,我们可能会尝试这样做。

long start = System.currentTimeMillis();
// ...
long finish = System.currentTimeMillis();
long timeElapsed = finish - start;

If we look at the code it makes perfect sense. We get a timestamp at the start and we get another timestamp when the code finished. Time elapsed is the difference between these two values.

如果我们看一下代码,它是完全有意义的。我们在开始时得到一个时间戳,当代码完成时我们得到另一个时间戳。经过的时间是这两个值之间的差异。

However, the result may and will be inaccurate as System.currentTimeMillis() measures wall-clock time. Wall-clock time may change for many reasons, e.g. changing the system time can affect the results or a leap second will disrupt the result.

然而,由于System.currentTimeMillis()测量的是挂钟时间,所以结果可能也会不准确。挂钟时间可能因多种原因而改变,例如,改变系统时间会影响结果,或者闰秒会破坏结果。

2.2. nanoTime()

2.2. nanoTime()

Another method in java.lang.System class is nanoTime(). If we look at the Java documentation, we’ll find the following statement:

java.lang.System类中的另一个方法是nanoTime()。如果我们看一下Java文档,我们会发现以下语句。

“This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.”

“这种方法只能用于测量经过的时间,与任何其他系统或挂钟的时间概念无关。”“这种方法只能用于测量经过的时间,与任何其他系统或挂钟的时间概念无关。

Let’s use it:

让我们来使用它。

long start = System.nanoTime();
// ...
long finish = System.nanoTime();
long timeElapsed = finish - start;

The code is basically the same as before. The only difference is the method used to get timestamps – nanoTime() instead of currentTimeMillis().

代码基本上和以前一样。唯一的区别是用于获取时间戳的方法–nanoTime()而不是currentTimeMillis()

Let’s also note that nanoTime(), obviously, returns time in nanoseconds. Therefore, if the elapsed time is measured in a different time unit we must convert it accordingly.

我们还要注意,nanoTime(),显然是以纳秒为单位返回时间。因此,如果经过的时间是以不同的时间单位测量的,我们必须进行相应的转换。

For example, to convert to milliseconds we must divide the result in nanoseconds by 1.000.000.

例如,要转换为毫秒,我们必须将纳秒的结果除以1.000.000。

Another pitfall with nanoTime() is that even though it provides nanosecond precision, it doesn’t guarantee nanosecond resolution (i.e. how often the value is updated).

nanoTime()的另一个隐患是,尽管它提供了纳秒级的精度,但它并不保证纳秒级的分辨率(即值的更新频率)。

However, it does guarantee that the resolution will be at least as good as that of currentTimeMillis().

然而,它确实能保证分辨率至少与currentTimeMillis()的分辨率一样好。

3. Java 8

3.Java 8

If we’re using Java 8 – we can try the new java.time.Instant and java.time.Duration classes. Both are immutable, thread-safe and use their own time-scale, the Java Time-Scale, as do all classes within the new java.time API.

如果我们使用的是Java 8–我们可以试试新的java.time.Instantjava.time.Duration类。这两个类都是不可变的、线程安全的,并且使用它们自己的时间尺度,即Java Time-Scale,就像新的java.time API中的所有类一样。

3.1. Java Time-Scale

3.1.Java的时间尺度

The traditional way of measuring time is to divide a day into 24 hours of 60 minutes of 60 seconds, which gives 86.400 seconds a day. However, solar days are not always equally long.

测量时间的传统方法是将一天分为24小时60分60秒,这样一天就有86.400秒。然而,太阳日并不总是等长的。

UTC time-scale actually allows a day to have 86.399 or 86.401 SI seconds. An SI second is a scientific “Standard International second” and is defined by periods of radiation of the cesium 133 atom). This is required to keep the day aligned with the Sun.

UTC时间尺度实际上允许一天有86.399或86.401个SI秒。一个SI秒是科学上的 “标准国际秒”,由铯133原子的辐射周期来定义。)这是使一天与太阳保持一致的要求。

The Java Time-Scale divides each calendar day into exactly 86.400 subdivisions, known as seconds. There are no leap seconds.

Java时间尺度将每个日历日精确划分为86.400个小段,称为秒。不存在闰秒。

3.2. Instant Class

3.2.即时

The Instant class represents an instant on the timeline. Basically, it is a numeric timestamp since the standard Java epoch of 1970-01-01T00:00:00Z.

Instant类代表时间线上的一个瞬间。基本上,它是一个自标准Java纪元1970-01-01T00:00:00Z的数字时间戳。

In order to get the current timestamp, we can use the Instant.now() static method. This method allows passing in an optional Clock parameter. If omitted, it uses the system clock in the default time zone.

为了获得当前的时间戳,我们可以使用Instant.now()静态方法。这个方法允许传入一个可选的Clock参数。如果省略,它使用默认时区的系统时钟。

We can store start and finish times in two variables, as in previous examples. Next, we can calculate time elapsed between both instants.

我们可以在两个变量中存储开始和结束时间,就像之前的例子一样。接下来,我们可以计算这两个时刻之间的时间流逝。

We can additionally use the Duration class and it’s between() method to obtain the duration between two Instant objects. Finally, we need to convert Duration to milliseconds:

我们还可以使用Duration类和它的between()方法来获得两个 Instant对象之间的持续时间。最后,我们需要将Duration转换为毫秒。

Instant start = Instant.now();
// CODE HERE        
Instant finish = Instant.now();
long timeElapsed = Duration.between(start, finish).toMillis();

4. StopWatch

4.StopWatch

Moving on to libraries, Apache Commons Lang provides the StopWatch class that can be used to measure elapsed time.

接着是库,Apache Commons Lang提供了StopWatch类,可用于测量经过的时间。

4.1. Maven Dependency

4.1.Maven的依赖性

We can get the latest version by updating the pom.xml:

我们可以通过更新pom.xml获得最新版本。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

The latest version of the dependency can be checked here.

依赖关系的最新版本可以在这里检查。

4.2. Measuring Elapsed Time With StopWatch

4.2.用StopWatch测量经过的时间

First of all, we need to get an instance of the class and then we can simply measure the elapsed time:

首先,我们需要得到一个类的实例,然后我们就可以简单地测量耗费的时间。

StopWatch watch = new StopWatch();
watch.start();

Once we have a watch running, we can execute the code we want to benchmark and then at the end, we simply call the stop() method. Finally, to get the actual result, we call getTime():

一旦我们有了一个运行中的手表,我们就可以执行我们想做基准的代码,然后在最后,我们简单地调用stop()方法。最后,为了获得实际结果,我们调用getTime()

watch.stop();
System.out.println("Time Elapsed: " + watch.getTime()); // Prints: Time Elapsed: 2501

StopWatch has a few additional helper methods that we can use in order to pause or resume our measurement. This may be helpful if we need to make our benchmark more complex.

StopWatch有一些额外的辅助方法,我们可以使用这些方法来暂停或恢复我们的测量。如果我们需要使我们的基准更加复杂,这可能会有帮助。

Finally, let’s note that the class is not thread-safe.

最后,让我们注意到,这个类不是线程安全的。

5. Conclusion

5.结论

There are many ways to measure time in Java. We’ve covered a very “traditional” (and inaccurate) way by using currentTimeMillis(). Additionally, we checked Apache Common’s StopWatch and looked at the new classes available in Java 8.

在Java中,有很多方法来测量时间。我们通过使用currentTimeMillis()涵盖了一种非常 “传统”(而且不准确)的方式。此外,我们检查了Apache Common的StopWatch,并查看了Java 8中的新类。

Overall, for simple and correct measurements of the time elapsed, the nanoTime() method is sufficient. It is also shorter to type than currentTimeMillis().

总的来说,对于简单和正确地测量所经过的时间,nanoTime()方法是足够的。它也比currentTimeMillis()的输入时间短。

Let’s note, however, that for proper benchmarking, instead of measuring time manually, we can use a framework like the Java Microbenchmark Harness (JMH). This topic goes beyond the scope of this article but we explored it here.

然而,让我们注意到,为了进行适当的基准测试,我们可以使用像Java Microbenchmark Harness(JMH)这样的框架来代替手动测量时间。这个话题超出了本文的范围,但是我们在这里探讨了

Finally, as always, the code used during the discussion can be found over on GitHub.

最后,像往常一样,讨论中使用的代码可以在GitHub上找到