硬核的分布式概念,有关时间时钟问题和算法的问题( 三 )


  // or (c) just return obsv.  We use (c).   No loop is required although in some cases
  // we might discard a higher \"now\" value in deference to a slightly lower but freshly
  // installed obsv value.   That's entirely benign -- it admits no new orderings compared
  // to (a) or (b) -- and greatly reduces coherence traffic.
  // We might also condition (c) on the magnitude of the delta between obsv and now.
  // Avoiding excessive CAS operations to hot RW locations is critical.
  // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
  return (prev == obsv) ? now : obsv;

突出的第一件事是巨大的注解说明块 。 作为软件工程师 , 我们知道如果有很长的评论 , 那么必须要进行一些狡猾的事情 。
实际上 , 评论非常有趣 。 调用mach_absolute_time使用下面的RDTSC指令可能会导致在具有多个CPU插槽的机器上出现非单调行为 , 类似机械同情邮件列表上另一个发人深省的讨论 。
所以 , 至少 , 我们可以确信在MacOS上纳米时间总是单调的 , 对吧?实际上 , 它取决于JVM版本 。
上面列出的代码是在JDK-8040140的 JDK9中引入的 , 并且后向兼容到JDK8 。

当毫秒不够时
在微秒精度的情况下gettimeofday远远超过System.currentTimeMillis() , 但在转换过程中精度丢失 。
jlong os::javaTimeMillis() {
  timeval time;
  int status = gettimeofday(&time NULL);  assert(status != -1 \"linux error\");  return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
                                                      // ^^ precision loss


操作系统可以为我们提供额外的信息 , 我们会将其暴力丢弃 , 以便将其整合到一个jlong内 。
如果我们真的想知道这些微观怎么办?在JDK 8中 , 新的JSR 310到达 , 这使得有可能获得一个Instant类的实例 , 其中包含自纪元以来的秒数和自上一秒开始以来的纳秒数 。

JSR 310:日期和时间API
Instant instant = Clock.systemUTC().instant();long epochSecond = instant.getEpochSecond();int nanoSinceSecond = instant.getNano();


最后 , 所有Java开发人员都可以高精度地访问挂钟时间 , 对吧?
不是那么快 , 如果我们看看JDK8中的实现 , 我们会发现它只是直接代表System.currentTimeMillis() 。
JDK8时钟
@Overridepublic long millis() {    return System.currentTimeMillis();
@Overridepublic Instant instant() {    return Instant.ofEpochMilli(millis());



显然 , 这不是最优的 , 并且相应的问题JDK-8068730已经解决 , 因此精度提高了 。 它需要对JDK9 +进行更新 , 其中该方法在Linux上使用以下实现委托给本机调用 。 假设您的操作系统可以提供微秒分辨率 , 这个时钟就是具有纳秒精度的时钟的一个很好的例子 , 但只有微秒的分辨率 。
JDK9 +时钟
void os::javaTimeSystemUTC(jlong &seconds jlong &nanos) {
  timeval time;
  int status = gettimeofday(&time NULL);  assert(status != -1 \"linux error\");
  seconds = jlong(time.tv_sec);
  nanos = jlong(time.tv_usec) * 1000;


时间交换
以微秒分辨率获得当前挂钟时间的可能性很大 , 但经常需要吗?
使用挂钟时间的原因之一是能够将在一台机器上发生的事件与在不同机器上发生的另一事件相关联 , 或者更准确地说 , 决定这些事件的顺序 。
这些事件的性质可能非常不同 。
其中一些可能不是非常关键 , 例如日志行上的时间戳 , 但其中一些必须是正确的 , 例如由于同时写入两个值而数据库中存在冲突 , 并且时间戳用于确定哪个事件是持续 。 这种策略称为Last Write Wins , 或简称为LWW 。

推荐阅读