You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JDK中nanoTime与currentTimeMillis时间漂移问题及实现原理问询

Windows下JDK11+中System.nanoTime与System.currentTimeMillis的时间偏差问题

以起始点为基准,使用nanoTime与currentTimeMillis进行的时间测量值最多三分钟后会出现偏差。

该问题存在于JDK11及以上版本的Windows实现中,可能更早版本也受影响,但Linux似乎不受影响。由于最可能的问题根源在Windows,且认为currentTimeMillis是稳定的时间函数,而nanoTime存在偏差,因此想了解Java获取这两类时间戳的方式。

在Linux系统下,两者的差异源于毫秒转纳秒的换算方式,差异表现稳定且在毫秒级精度范围内。

由于两个值都是基于各自的起始点计算的,且起始点的获取间隔仅为几微秒,原本预期它们会像Linux下一样保持毫秒级精度。计时器的节奏异常本不应造成影响,因为该计时器仅用于提供缓慢、可读的测量进度,无需精确计时。

import java.text.*;
import java.util.*;
import java.util.concurrent.*;

public class ClockWatch {

    static final long StartNS = System.nanoTime();
    static final long StartMS = System.currentTimeMillis();

    public static void main(String[] args) {
        NumberFormat nf = NumberFormat.getNumberInstance( Locale.US);
        nf.setMaximumFractionDigits( 3);
        nf.setGroupingUsed( true);

        DateFormat df = new SimpleDateFormat( "HH:mm:ss");
        df.setTimeZone( TimeZone.getTimeZone( "GMT"));
        ScheduledExecutorService ses = new ScheduledThreadPoolExecutor( 1);

        Runnable compareJob = () -> {
            long nowNS = System.nanoTime() - StartNS;
            long nowMS = System.currentTimeMillis() - StartMS;
            long tookNS = ( System.nanoTime() - StartNS) - nowNS;
            long diffNS = 1_000_000 * nowMS - nowNS;
            double driftPercent = 100.0 * diffNS / nowNS;
            System.out.println( df.format( new Date( nowMS))
                + " currentTimeMillis is "
                + ( diffNS < 0 ? "" : " ")
                + nf.format( 1e-6 * diffNS)
                + " ms faster than nanoTime, took "
                + nf.format( tookNS) + " ns, drift "
                + nf.format( driftPercent) + " % since start"
            );
        };
        ses.scheduleAtFixedRate( compareJob, 1, 3, TimeUnit.SECONDS);
    }
}

我对此问题感到困扰的原因是,希望复用已有的纳秒时间戳,将其转换为等效的currentTimeMillis值,而无需调用currentTimeMillis方法。我知道,在Windows最新硬件上该方法仅需4纳秒。


所有机器已运行多日,借助chrony和ntp实现了亚毫秒级的时钟精度,因此不会出现突然跳变。

我了解nanoTime和currentTimeMillis的特性,或许会改用Instant.now替代两者。

核心需求是使用log4j2(配置了完整时间戳)记录事件。多线程异步向同一文件日志,这是一台大型机器。在某些情况下,由于log4j处理不及时,日志线程会出现阻塞,导致日志线程变慢。为缓解此问题,我将log4j调用移至由另一个线程处理的队列中。由于预期该线程会产生队列延迟,我希望在日志消息中添加另一个时间戳,反映事件被加入日志队列的时间。对于这个内部时间戳,我已有一个纳秒时间戳,希望将其复用为与log4j时间戳匹配的格式。

目前我通过获取另一个currentTimeMillis时间戳解决了问题,但这需要在许多对象中添加一个long字段并增加一次测量,这两者都是我想避免的。由于需要测量值与log4j使用的时间兼容,我现在似乎需要尝试使用Instant.now。

内容的提问来源于stack exchange,提问作者Ralf H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:15:53