如何在Java中计算事件的经过时间?

57

有什么简单/易用的方法使用Java访问系统时钟,以便我可以计算事件的经过时间吗?

6个回答

85

我建议不要使用 System.currentTimeMillis() 来衡量经过的时间。currentTimeMillis() 返回 '挂钟' 时间,这可能会更改(例如:夏令时,管理员用户更改时间),从而影响您的间隔测量。

另一方面,System.nanoTime() 返回自某个参考点(例如 JVM 启动)以来的纳秒数,因此不会受到系统时钟更改的影响。


11
我认为夏令时不会影响currentTimeMillis()函数,因为它是基于UTC定义的。请注意,这里只是翻译,没有额外的解释或其他内容。 - Mr. Shiny and New 安宇
@Shiny:UTC 的确是个好点子——我之前没有意识到。不过,我认为我的回答主要观点仍然成立,即 currentTimeMillis() 受墙钟变化的影响而偏差较大。NTP 更新也可能是 currentTimeMillis() 出现问题的另一个原因。 - Leigh
System.nanoTime() 对 CPU 频率变化也具有抵抗力吗? - akarnokd
@kd304,我相信它是的;它也应该能够在计算机休眠和唤醒时保持稳定。如果不行,那就是一个bug。 - Kevin Bourrillion
2
在我回答一个重复的问题时,我对此进行了更详细的解释,请参见:https://dev59.com/WHI-5IYBdhLWcg3wm5pH#1776053 - Kevin Bourrillion

27

这是一些示例代码。

long startTime = System.currentTimeMillis();
// Run some code;
long stopTime = System.currentTimeMillis();

System.out.println("Elapsed time was " + (stopTime - startTime) + " miliseconds.");

我的编辑注释是“修复方法名称'currentTimeMilis'-> 'currentTimeMillis'。使代码可复制/粘贴。(FWIW)”,但找不到其他5个字符进行编辑:( - Diederik
如果在你的“运行一些代码”期间,机器是Windows 10或更高版本,并且在Thread.sleep()期间进入S3睡眠状态,我认为这不会起作用。请参见:https://dev59.com/sF0b5IYBdhLWcg3wQPbw - Dale

9

更新链接:https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/time/StopWatch.html - nasch

8

Leigh的回答是正确的。

java.time

Java 8及更高版本已内置java.time框架。 Instant是UTC时间轴上的一个瞬间,具有纳秒分辨率(最多9个小数位)。now方法获取当前日期时间瞬间。
Instant now = Instant.now();

2016-03-12T04:29:39.123Z

你可以将一对Instant对象之间的经过时间计算为Duration。该持续时间使用纳秒分辨率,最大值为long型可以容纳的秒数。这比当前宇宙估计年龄还要大。
Duration duration = Duration.between( startInstant , stopInstant );

Duration::toString 的默认输出格式为标准的 ISO 8601 格式。您还可以请求纳秒总数 (toNanos) 或毫秒总数 (toMillis),以及其他数量。

Java 8

在Java 8中,获取当前时刻只能以毫秒分辨率(最多三位小数)解析。因此,虽然java.time类可以存储纳秒,但它们只能用毫秒确定当前时刻。这种限制是由于遗留问题(默认Clock实现使用System.currentTimeMillis())。

Java 9

在Java 9及更高版本中,默认的Clock实现可以以纳秒分辨率确定当前时刻。实际上,这取决于计算机时钟硬件的精度。

有关更多信息,请参见此OpenJDK问题页面:增加java.time.Clock.systemUTC()实现的精度

微基准测试

如果您的目的是进行基准测试,请确保查看其他问题,例如:

有可用的框架可协助进行短期基准测试。


5

java.lang.System.currentTimeMillis()java.lang.System.nanoTime() 可以用来测量经过的时间。


请记住,尽管这些函数返回以毫秒和纳秒为单位测量的值,但时钟分辨率可能不是那么高。15毫秒的时钟分辨率非常普遍 - 在计时非常短的事件时,请记住这一点。 - Adam Rosenfield
我在Windows上使用LockSupport.parkNanos()有很好的解决方案经验 - 但是我承认这不是这个问题的秒表候选项。 - akarnokd

3
这是我使用System.nanoTime()编写的小型StopWatch类,正如Leigh在答案中建议的那样:
public class StopWatch {
    // Constructor
    public StopWatch() {
    }

    // Public API
    public void start() {
        if (!_isRunning) {
            _startTime = System.nanoTime();
            _isRunning = true;
        }
    }

    public void stop() {
        if (_isRunning) {
            _elapsedTime += System.nanoTime() - _startTime;
            _isRunning = false;
        }
    }

    public void reset() {
        _elapsedTime = 0;
        if (_isRunning) {
            _startTime = System.nanoTime();
        }
    }

    public boolean isRunning() {
        return _isRunning;
    }

    public long getElapsedTimeNanos() {
        if (_isRunning) {
            return System.nanoTime() - _startTime;
        }
        return _elapsedTime;
    }

    public long getElapsedTimeMillis() {
        return getElapsedTimeNanos() / 1000000L;
    }

    // Private Members
    private boolean _isRunning = false;
    private long _startTime = 0;
    private long _elapsedTime = 0;
}

我建议添加获取秒值的便捷方法和一个带有布尔标志的构造函数,该标志将允许秒表立即启动。至少这是我在使用您的类之后所需要的 :) - Dariusz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接