Java中的纳秒

前言

最近在使用InfluxDB保存系统的操作日志,如果在插入的时候不指定time字段,influxDB会默认设置time,但一般来说,数据经过多方周转如果采用默认的插入时间,对业务来说误差较高。我们的日志是发送给kafka,然后消费者订阅kafka消费数据再保存到influxDB,数据会有延迟。因此会在数据产生的时候写入time字段值,对时间的精度要求比较高,这样才能有效防止数据丢失。
Java中时间精度最高的应该是纳秒了,所以查询了下获取纳秒的一些方法。

JDK8获取纳秒的问题

JDK8中提供了丰富的时间的工具类,但是实际上JDK8中没有真正获取纳秒的方法,可查看这篇文章,https://stackoverflow.com/questions/1712205/current-time-in-microseconds-in-java,
Instant类中可以获取纳秒的方法,本质上是获取的毫秒,后6位都是0。

Instant instant = Instant.now();
int tt = instant.getNano(); 
System.out.println(tt); 
//返回的时间为576000000,后6位都是0>

查看java.time.Clock类的源码可发现,本质是获取当前的System.currentTimeMillis()生成时间类的。

    static final class SystemClock extends Clock implements Serializable {
        private static final long serialVersionUID = 6740630888130243051L;
        private final ZoneId zone;

        SystemClock(ZoneId zone) {
            this.zone = zone;
        }
        @Override
        public ZoneId getZone() {
            return zone;
        }
        @Override
        public Clock withZone(ZoneId zone) {
            if (zone.equals(this.zone)) {  // intentional NPE
                return this;
            }
            return new SystemClock(zone);
        }
        @Override
        public long millis() {
            return System.currentTimeMillis();
        }
        @Override
        public Instant instant() {
            return Instant.ofEpochMilli(millis());//本质是获取当前的毫秒时间戳生成时间类的
        }
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof SystemClock) {
                return zone.equals(((SystemClock) obj).zone);
            }
            return false;
        }
        @Override
        public int hashCode() {
            return zone.hashCode() + 1;
        }
        @Override
        public String toString() {
            return "SystemClock[" + zone + "]";
        }
    }

Jdk还有一个方法System.nanoTime()可以提供纳秒的精度测量的方法,一般可以用来计算2个方法之间的差值,不能直接转成时间戳。

long startTime = System.nanoTime();
// ... the code being measured ...
long elapsedNanos = System.nanoTime() - startTime;
//To compare elapsed time against a timeout, use

可以利用这个特性,初始化t1,获取差值从而获取纳秒。

t1=System.nanoTime(),
offset=System.currentTimeMillis()*1_000_000 - t1,
//获取当前的纳秒
currentNano=System.nanoTime() + offset

https://github.com/jenetics/jenetics/blob/master/jenetics/src/main/java/io/jenetics/util/NanoClock.java 就是利用这个方法实现的。

JDK9之后获取纳秒的问题

之前有提过这方面的缺陷,https://bugs.openjdk.java.net/browse/JDK-8068730,JDK9之后优化了获取纳秒的问题。

Instant instant = Instant.now();
int tt = instant.getNano(); 
System.out.println(tt); 
//返回的时间为72765400,已经包含了纳秒。

java.time.Clock源码里,增加了VM.getNanoTimeAdjustment方法获取纳秒。

        public Instant instant() {
            // Take a local copy of offset. offset can be updated concurrently
            // by other threads (even if we haven't made it volatile) so we will
            // work with a local copy.
            long localOffset = offset;
            long adjustment = VM.getNanoTimeAdjustment(localOffset);

            if (adjustment == -1) {
                // -1 is a sentinel value returned by VM.getNanoTimeAdjustment
                // when the offset it is given is too far off the current UTC
                // time. In principle, this should not happen unless the
                // JVM has run for more than ~136 years (not likely) or
                // someone is fiddling with the system time, or the offset is
                // by chance at 1ns in the future (very unlikely).
                // We can easily recover from all these conditions by bringing
                // back the offset in range and retry.

                // bring back the offset in range. We use -1024 to make
                // it more unlikely to hit the 1ns in the future condition.
                localOffset = System.currentTimeMillis()/1000 - 1024;

                // retry
                adjustment = VM.getNanoTimeAdjustment(localOffset);

                if (adjustment == -1) {
                    // Should not happen: we just recomputed a new offset.
                    // It should have fixed the issue.
                    throw new InternalError("Offset " + localOffset + " is not in range");
                } else {
                    // OK - recovery succeeded. Update the offset for the
                    // next call...
                    offset = localOffset;
                }
            }
            return Instant.ofEpochSecond(localOffset, adjustment);
        }

这时候如要获取纳秒的时间戳就简单了。

Instant instant = Instant.now();
int tt = instant.getNano();                // Represent a moment in UTC.
long time = instant.toEpochMilli()/1000*1000_000_000 + tt;
System.out.println(time);

参考资料

  1. https://stackoverflow.com/questions/1712205/current-time-in-microseconds-in-java
  2. https://bugs.openjdk.java.net/browse/JDK-8068730
  3. https://github.com/jenetics/jenetics/blob/master/jenetics/src/main/java/io/jenetics/util/NanoClock.java
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值