目录
问题内容
解决方法
...但我需要单调递增的时间戳!
currenttimemillis
nanotime
问题
首页 Java 为什么在大时间窗口上计算的经过时间在 System.currentTimeMillis 与 System.nanoTime 之间有高达 100+ 毫秒的差异

为什么在大时间窗口上计算的经过时间在 System.currentTimeMillis 与 System.nanoTime 之间有高达 100+ 毫秒的差异

Feb 09, 2024 am 10:24 AM
overflow

php小编苹果为大家解答为什么在大时间窗口上计算的经过时间在 System.currentTimeMillis 与 System.nanoTime 之间会有高达 100+ 毫秒的差异。这个问题涉及到计算机系统的底层机制和操作系统的调度方式。在计算经过时间时,System.currentTimeMillis 使用的是操作系统提供的墙钟时间,而System.nanoTime 使用的是CPU时钟周期。由于操作系统的调度机制和硬件时钟的异步性,这两种计时方法会导致时间差异。具体的原因和解决方案将在接下来的文章中详细介绍。

问题内容

我正在尝试创建一个可在同一 jvm 中进行微秒进动排序的 ksuid,因为 currenttimemillis 仅给出毫秒精度,考虑将 currenttimemillis() 和 nanotime() 一起使用,收集第一个瞬间和第二个瞬间开始的值计算自第一个时间瞬间以来经过的微秒数的 nanotime 差异。使用从 nanotime 计算出的经过时间并将其添加到初始即时毫秒中,以便我们可以在 jvm 启动后的任何即时时间(均在同一 jvm 内)以微秒精度计算 epoch。

//静态收集初始nanotime和currentmillis 最终长 inittimenanos = system.nanotime();最终长 inittimemillis = system.currenttimemillis();

//从这里开始,当需要微/纳秒精度的当前时间时,从nanotime计算经过的时间并将经过的毫秒添加到initialmillis,剩下的经过时间给出微秒精度的时间

final long elapsednanotime = system.nanotime() - inittimenanos;最终双精度 elapsedmillisfromnanotime = elapsednanos / (1000000.0);

//自jvm启动以来单调递增的时间戳毫秒(epoch,我们可能不称其为epoch) final 长计算当前时间millis = inittimemillis + elapsedmillisfromnanotime; final long nanosprecision = elapsednanos % 1000000; //来自nanotime的附加时间精度

考虑使用这些值来生成单调递增的 ksuid,它可以近似表示当前时间,但保证根据同一 jvm 内的创建时间进行排序,因为 currenttimemillis 不提供单调递增的时间戳保证,考虑使用这种方法生成大约接近真实时间戳的单调递增时间戳(可能有几毫秒的差异,除非对纪元时间进行闰秒调整)。使用 epoch 和 nanotime 计算出的经过时间预计有几毫秒的差异,但实际差异非常频繁地变化,当我在测试以下运行 48 小时时,观察到这两者之间的差异高达 150 毫秒。大多数情况下,使用 nanotime 计算的经过时间高于使用 currenttimemillis 计算的经过时间,并且观察到的时间范围为 -2 毫秒到 +150 毫秒。

final long elapsedmillis = system.currenttimemillis() - inittimemillis; //来自system.currenttimemillis()的经过时间毫秒 最终双方差millis = elapsedmillisfromnanotime - elapsedmillis; //经过时间方差

我是否遗漏了有关 jvm 时间保证的任何内容,计算单调递增近似时间戳的方法是否错误?(这不会用作系统中的真实纪元,仅用于生成 uuid,uuid 也代表同一 jvm 内的近似时间戳即时)。

//测试类

package org.example;

import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) throws Exception {
        final long initTimeNanos = System.nanoTime();
        final long initTimeMillis = System.currentTimeMillis();
        System.out.println("Nanos: " + initTimeNanos);
        System.out.println("Millis: " + initTimeMillis);

        while (true) {
            final long currentNanos = System.nanoTime();
            final long elapsedNanos = currentNanos - initTimeNanos;
            final double elapsedMillisFromNanos = elapsedNanos / 1000000.0;
            final long elapsedMillis = System.currentTimeMillis() - initTimeMillis;
            final double varianceMillis = elapsedMillisFromNanos - elapsedMillis;
            if (Math.abs(varianceMillis) > 1) {
                System.out.printf("\nVariance Observed: %.6f\n", varianceMillis);
                System.out.printf("Elapsed Time: %.6fms (from System.nanoTime)\n", elapsedMillisFromNanos);
                System.out.printf("Elapsed Time: %dms (from System.currentTimeMillis)\n", elapsedMillis);
            }
            if (elapsedMillis > TimeUnit.HOURS.toMillis(48)) {
                break;
            }
            Thread.sleep(5000);
            System.out.print(".");
        }
    }
}
登录后复制

为什么经过的时间方差不断变化? 如果 jvm 连续运行一年,我们可以预期的最大方差是多少(任何 jvm 对此上限或下限的保证,使用 mac 和 windows 进行测试,mac 给出了方差的缓慢增长,windows 的速度要快得多)?

我预计经过的时间变化小于 10 毫秒,变化变化频率较低。 但实际观察是方差不断变化,上下波动,48小时内观察到最多150毫秒

解决方法

一种解释是基于 ntp 的时间涂抹。更一般地说,纳米时间和 ctm 测量完全不同的东西,你不能将它们混合。

nanotime 有一个任意的 0 点(获得 0 的 nanotime 没有特殊意义),因此,除了将它返回的内容与不同 nanotime 调用的结果进行比较之外,根本没有必要调用它。 nanotime 跟踪经过的时间,就是这样。

system.ctm 获取系统时钟。如果您使用 posix date 命令或在系统设置中编辑系统的时间设置,则不会影响 nanotime,但会更改 system.ctm 返回的内容。 ctm 通常也比 nanotime 慢得多。 ctm 还有一个明确定义的 0 - 表示 utc 1970 年 1 月 1 日午夜。

问题:“我希望当前时间符合纳秒精度的系统时钟”在 jvm 上不可能

时间涂抹是指某些网络时间守护程序注意到您的系统时钟略有偏差,并且不只是将系统时钟编辑到正确的时间,而是引入了涂抹:假设您比实时时间“提前”了 400 毫秒。 ntp 可以将您的时钟向后设置 400 毫秒,但许多日志记录系统假设 system.ctm 不会向后移动(这是一个不正确但广泛应用的假设)。

这可以通过让时间放慢一段时间来“修复”:每经过 100 毫秒,ntp 守护进程就会将时钟“保持”在同一毫秒上一毫秒。每 100 毫秒就赶上 1 毫秒,因此在 400,000 毫秒(仅 400 秒)内,时钟恢复与网络同步,并且日志记录根本不受影响。

但是,这显然会完全破坏 system.ctm 和 nanotime 之间的任何关系!

大多数 ntp 都是这样涂抹的(它们也会向前涂抹 - 如果您的系统时钟落后,它不仅仅会向前跳跃:这会使日志撒谎(使其看起来就像两个事件之间存在一些间隙)比实际大得多),因此每 100 毫秒,ntp 就会使时钟跳过一毫秒,类似这样的事情,以赶上。

...但我需要单调递增的时间戳!

那么 nanotime 就无关紧要了。不要使用它。

拥有一些提供 id 的集中“商店”。一种实现:

class TimeStore {
  long lastVal = 0L;

  public synchronized long get() {
    long mark = System.currentTimeMillis() << 4;
    return lastVal = Math.max(mark, lastVal + 1);
  }
}
登录后复制

这将返回当前时间,左移 4 位,并将填充此移位“释放”的 16 个值,以便能够在同一时间生成单调递增值,最多 16 次;同一毫秒内的任何进一步请求都会潜入下一毫秒。

尽管如此,这可能并不比nanotime慢。

rzwitserloot的回答是正确的。我将向您提供我对各种问题的看法。

currenttimemillisnanotime 无关

system.currenttimemillissystem.nanotime 彼此无关。

  • currenttimemillis 从主机的硬件时钟单元检索当前日期和时间,由主机操作系统管理。
  • nanotime 来自主机 cpu 保存的计数。

currenttimemillis

所以首先要了解人类的年月日和时分秒的概念。传统计算机中使用的时钟硬件的分辨率有限,有些是毫秒,有些是微秒,但没有纳秒。

由于各种原因,本次通话报告的日期和时间可能会有所不同。其中一个原因是,在电池电量耗尽的情况下启动的计算机会将其时钟重置为默认时刻,直到通过调用时间服务器进行纠正。另一个原因是系统管理员或用户可能会更改日期时间。还有一个原因是硬件时钟可能无法很好地保持时间,并且会通过调用时间服务器在更新之间运行得快或慢。

nanotime

nanotime 调用以纳秒计数形式告知经过的时间。但这个流逝的时间与日历和墙上的时钟无关。此调用仅返回经过的纳秒的单调计数。

这个计数非常准确,因为它来自计算机的“跳动的心脏”。返回的数量始终在增加,直到达到 long.max_value,然后环绕到 long.min_value。这给出了大约 292 年的范围,但这并不意味着从现在起 292 年。计数的起点未指定。在某些 java 实现中,您可能会看到计数在计算机启动时开始。但并不能保证这一点。

问题

您可能会考虑寻找 新 6、7、8 版本 的实现a href="https://en.wikipedia.org/wiki/universally_unique_identifier" rel="nofollow noreferrer">uuid 被提议给 ietf 进行标准化。

currenttimemillis 在现代 java 中已被 java.time.instant 类取代。调用 instant.now 捕获 utc 中的当前时刻。

java 9+ 中的常见实现以微秒为单位报告,但在 java 8 中以毫秒为单位。 instant 类能够以纳秒为单位。

聪明的主意。但不现实。如上所述,currenttimemillisnanotime 不相关。最重要的是,currenttimemillis 的结果可能会因多种原因而有所不同。同时,nanotime 计数可能会随着主机的每次启动而变化,并且肯定不会对应于其他计算机上进行的任何 nanotime 调用。

是的,您忽略了这样一个事实:java 规范没有做出您似乎假设的任何保证。

javadoc 中唯一的保证是 nanotime 是“高分辨率”,至少与 currenttimemillis 一样好。并且无法保证您预计何时能迎来 292 年的终结。

同时,currenttimemillis是一个移动目标,可以随时改变,向前或向后移动。

这是不可预测的。

java 规范没有做出这样的保证。

以上是为什么在大时间窗口上计算的经过时间在 System.currentTimeMillis 与 System.nanoTime 之间有高达 100+ 毫秒的差异的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

比特币诞生至今价格2009-2025 最全的BTC历史价格汇总 比特币诞生至今价格2009-2025 最全的BTC历史价格汇总 Jan 15, 2025 pm 08:11 PM

自 2009 年问世以来,比特币成为加密货币界的领头羊,其价格经历了巨大的波动。为了提供全面的历史概述,本文汇集了从 2009 年到 2025 年的比特币价格数据,涵盖了重大的市场事件、市场情绪变化和影响价格走势的重要因素。

比特币诞生至今历史价格总览 比特币历史价格趋势大全 比特币诞生至今历史价格总览 比特币历史价格趋势大全 Jan 15, 2025 pm 08:14 PM

比特币,作为一种加密货币,自问世以来经历了显着的市场波动。本文将提供比特币自诞生以来的历史价格总览,帮助读者了解其价格趋势和关键时刻。通过分析比特币的历史价格数据,我们可以了解市场对其价值评估、影响其波动的因素,并为未来投资决策提供依据。

比特币诞生至今历史价格一览 BTC历史价格行情趋势图(最新汇总) 比特币诞生至今历史价格一览 BTC历史价格行情趋势图(最新汇总) Feb 11, 2025 pm 11:36 PM

比特币自 2009 年创世以来,价格经历多次大幅波动,最高涨至 2021 年 11 月的 69,044.77 美元,最低跌至 2018 年 12 月的 3,191.22 美元。截至 2024 年 12 月,最新价格突破 100,204 美元。

2018-2024年比特币最新价格美元大全 2018-2024年比特币最新价格美元大全 Feb 15, 2025 pm 07:12 PM

实时比特币美元价格 影响比特币价格的因素 预测比特币未来价格的指标 以下是 2018-2024 年比特币价格的一些关键信息:

如何通过CSS自定义resize符号并使其与背景色统一? 如何通过CSS自定义resize符号并使其与背景色统一? Apr 05, 2025 pm 02:30 PM

CSS自定义resize符号的方法与背景色统一在日常开发中,我们经常会遇到需要自定义用户界面细节的情况,比如调...

Flex布局下文字超出省略却撑开容器?如何解决? Flex布局下文字超出省略却撑开容器?如何解决? Apr 05, 2025 pm 11:00 PM

Flex布局下文字超出省略导致容器撑开的问题及解决方法在使用Flex...

H5页面制作是前端开发吗 H5页面制作是前端开发吗 Apr 05, 2025 pm 11:42 PM

是的,H5页面制作是前端开发的重要实现方式,涉及HTML、CSS和JavaScript等核心技术。开发者通过巧妙结合这些技术,例如使用&lt;canvas&gt;标签绘制图形或使用JavaScript控制交互行为,构建出动态且功能强大的H5页面。

如何使用CSS的clip-path属性实现分段器的45度曲线效果? 如何使用CSS的clip-path属性实现分段器的45度曲线效果? Apr 04, 2025 pm 11:45 PM

如何实现分段器的45度曲线效果?在实现分段器的过程中,如何让点击左侧按钮时右侧边框变成45度曲线,而点�...