为什么JDK源代码采用`volatile`实例的`final`副本

时间:2012-10-31 10:29:42

标签: java

我阅读了JDK关于ConcurrentHashMap的源代码。

但以下代码让我感到困惑:

public boolean isEmpty() {
    final Segment<K,V>[] segments = this.segments;
    ...
}

我的问题是:

宣布“this.segments”:

final Segment<K,V>[] segments;

所以,在这里,在方法的开头,声明了一个相同的类型引用,指向相同的内存。

为什么作者这样写?他们为什么不直接使用this.segments?有什么理由吗?

3 个答案:

答案 0 :(得分:94)

这是涉及volatile变量的无锁代码的典型惯用语。在第一行,您阅读volatile一次,然后使用它。在此期间,另一个线程可以更新volatile,但您只对最初阅读的值感兴趣。

此外,即使有问题的成员变量不是易变的而是最终的,这个习惯用法与CPU缓存有关,因为从堆栈位置读取比从随机堆位置读取更加缓存友好。本地var最终可能最终绑定到CPU寄存器。

对于后一种情况,实际上存在一些争议,因为JIT编译器通常会处理这些问题,但Doug Lea是坚持一般原则的人之一。

答案 1 :(得分:19)

我想这是出于性能考虑,因此我们只需要检索字段值一次。

你可以参考Joshua Bloch的有效java中的单身成语

他的单身人士在这里:

private volatile FieldType field;
FieldType getField() {
  FieldType result = field;
  if (result == null) { 
    synchronized(this) {
      result = field;
      if (result == null) 
        field = result = computeFieldValue();
    }
  }
  return result;
}

他写道:

  

此代码可能看起来有点复杂。特别需要的   局部变量结果可能不清楚。这个变量的作用是什么   确保该字段在常见情况下只读取一次   已初始化。 虽然不是绝对必要,但这可能会有所改善   通过适用于低级别的标准,性能更优雅   并发编程。在我的机器上,上面的方法大约是25   比没有局部变量的明显版本快

答案 2 :(得分:4)

它可能会减少字节代码大小 - 访问本地变量的字节代码比访问实例变量要短。

运行时优化开销也可能会降低。

但这些都不重要。它更多的是代码风格。如果您对实例变量感到满意,那么无论如何。 Doug Lea可能感觉更容易处理局部变量。