28

Java 8 的 java.time.Instant 以“纳秒分辨率”存储,但使用 Instant.now() 仅提供毫秒分辨率...

Instant instant = Instant.now();
System.out.println(instant);
System.out.println(instant.getNano());

结果...

2013-12-19T18:22:39.639Z
639000000

如何获得值为“现在”但具有纳秒分辨率的 Instant?

4

5 回答 5

21

虽然默认 Java8 时钟不提供纳秒分辨率,但您可以将其与 Java 功能结合使用以纳秒分辨率测量时间差异,从而创建一个实际的纳秒时钟。

public class NanoClock extends Clock
{
    private final Clock clock;

    private final long initialNanos;

    private final Instant initialInstant;

    public NanoClock()
    {
        this(Clock.systemUTC());
    }

    public NanoClock(final Clock clock)
    {
        this.clock = clock;
        initialInstant = clock.instant();
        initialNanos = getSystemNanos();
    }

    @Override
    public ZoneId getZone()
    {
        return clock.getZone();
    }

    @Override
    public Instant instant()
    {
        return initialInstant.plusNanos(getSystemNanos() - initialNanos);
    }

    @Override
    public Clock withZone(final ZoneId zone)
    {
        return new NanoClock(clock.withZone(zone));
    }

    private long getSystemNanos()
    {
        return System.nanoTime();
    }
}

使用它很简单:只需为 Instant.now() 提供额外的参数,或直接调用 Clock.instant():

    final Clock clock = new NanoClock();   
    final Instant instant = Instant.now(clock);
    System.out.println(instant);
    System.out.println(instant.getNano());

尽管即使您每次都重新创建 NanoClock 实例,此解决方案也可能有效,但最好还是坚持使用在代码中早期初始化的存储时钟,然后在需要的地方使用它。

于 2016-07-29T11:48:06.417 回答
18

如果您获得毫秒级的分辨率,您可以认为自己很幸运。

Instant可以将时间建模到纳秒精度,但至于实际分辨率,它取决于底层操作系统的实现。例如,在 Windows 上,分辨率非常低,大约为 10 毫秒。

将此与 比较System.nanoTime(),它给出了以微秒为单位的分辨率,但没有给出绝对的挂钟时间。显然,为您提供这种分辨率,仍然需要权衡取舍,但仍比纳秒短三个数量级。

于 2013-12-19T18:39:27.817 回答
9

所以我花了一些时间在这里挖掘 Javadoc:

http://download.java.net/jdk8/docs/api/java/time/Instant.html

看来您应该能够执行以下操作:

Instant inst = Instant.now();
long time = inst.getEpochSecond();
time *= 1000000000L; //convert to nanoseconds
time += inst.getNano(); //the nanoseconds returned by inst.getNano() are the nanoseconds past the second so they need to be added to the epoch second

也就是说 - 其他回答者提出了一个很好的观点,即很难获得准确的纳秒时间,因为计算机通常没有能力跟踪该分辨率的时间

于 2013-12-19T18:42:20.687 回答
7

您只能通过使用 Instant-method 使用另一个更精确的 java.time.Clock 来获得具有“纳秒”的 Instantpublic static Instant now(Clock clock)在您的示例中,默认时钟通常使用 System.currentTimeMillis() ,它不能显示任何纳秒。

请注意,没有纳秒分辨率(实时)的时钟。java.time.Instant 的内部纳秒表示工具主要是由于需要映射以纳秒精度给出的数据库时间戳(但通常不精确到纳秒!)。

2015-12-29 更新: Java-9 将提供更好的时钟,请参阅我的较新帖子

于 2013-12-19T18:38:43.097 回答
0

我需要多个 JVM 实例通用的高精度计时器。

由于 JavadocSystem.nanoTime()明确指出每个 JVM 可能为此值使用不同的来源,我创建了以下测试类,它计算到 Epoch 的纳秒偏移量,因此我可以获得具有System.nanoTime()相同来源的精度的时间戳。(这与@logtwo 发布的 NanoClock 解决方案中提出的想法基本相同)

也许它有助于可视化Instant.now()&返回的值之间的分辨率差异,System.nanoTime()尽管两者名义上都具有纳秒精度。

在我正在使用的硬件上,这可能会导致 JVM 之间的原点出现大约 1 毫秒的增量,但这对于我的目的来说已经足够了。

import java.time.*;
import java.util.concurrent.TimeUnit;

public class EpochNanotimeOffset {

    private static final long NANO_TIME_EPOCH_OFFSET;
    /**/    static {
        final long     systemNanoTime         = System.nanoTime();
        final Instant  now                    = Clock.systemUTC().instant();
        final long     instantNanosSinceEpoch = Duration.between(Instant.EPOCH, now).toNanos();

        NANO_TIME_EPOCH_OFFSET = systemNanoTime - instantNanosSinceEpoch;
    }

    public static void main(final String[] args) throws InterruptedException {
        for (int i=0; i < 99; i++) {

            final long    systemNanoTime          = System.nanoTime();
            final Instant instant                 = Clock.systemUTC().instant();
            final long    instantNanos            = Duration.between(Instant.EPOCH, instant).toNanos();
            final long    correctedSystemNanoTime = systemNanoTime - NANO_TIME_EPOCH_OFFSET;
            final long    deltaNanos              = instantNanos   - correctedSystemNanoTime;

            Duration.between(Instant.EPOCH, instant).toNanos();

            System.out.print  (                  "OffsetNS=" + NANO_TIME_EPOCH_OFFSET );
            System.out.print  ('\t' + "instantNSsinceEpoch=" + instantNanos );
            System.out.print  ('\t' +   "correctedSystemNS=" + correctedSystemNanoTime );
            System.out.print  ('\t' +            "systemNS=" + systemNanoTime );
            System.out.println('\t' +             "deltaNS=" + deltaNanos );

//          TimeUnit.SECONDS.sleep(3);
        }
    }
}
于 2021-04-07T10:32:04.147 回答