我阅读了JDK关于ConcurrentHashMap的源代码。
但以下代码让我感到困惑:
public boolean isEmpty() {
final Segment<K,V>[] segments = this.segments;
...
}
我的问题是:
宣布“this.segments”:
final Segment<K,V>[] segments;
所以,在这里,在方法的开头,声明了一个相同的类型引用,指向相同的内存。
为什么作者这样写?他们为什么不直接使用this.segments?有什么理由吗?
答案 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可能感觉更容易处理局部变量。