JDK 1.6 和 JDK 1.7 中 ConcurrentHashMap 的不同“下一个”条目

Different `next` entry of ConcurrentHashMap in JDK 1.6 and JDK 1.7

在 JDK 1.6 中,Doug Lea 在 next 字段之前使用 final

static final class HashEntry<K,V> {
    final K key;
    final int hash;
    volatile V value;
    final HashEntry<K,V> next;

而在 JDK 1.7 中,next 字段前面有 volatile。我还注意到在JDK 1.7 中,get 方法采用getObjectVolatile 方法来读取value 字段,具有volatile 加载语义。

我不明白为什么 Doug Lea 以前使用 final。如果正确性有问题,那么他怎么能在JDK 1.7(还有JDK 1.8)中用volatile替换呢?

编辑:

具体来说,我的问题是我们可以在 JDK 1.6 的实现中用 volatile 替换 final 吗?

不是把final换成volatile的问题。与 RealSkeptic 一样,它在许多其他方法中发生了变化。目的可能是 ConcurrentHashMap.remove() 的优化。在 JDK1.6 中,存储桶(按哈希码分组的对象)的列表是 CopyOnWrite,因此在内部 remove() 部分列表被复制,在 JDK1.7 中仅 next 指针被更改。

第一个问题:

I have no sense why Doug Lea previously uses final. If there are problems with correctness, then how could he replace it with volatile in JDK 1.7(also JDK 1.8)?

这不是正确与否的问题。两种实现在线程安全方面都是正确的。试图解决的问题是减少 CHM 的初始足迹。在 Java 6 中,使 next 字段成为最终字段要求创建的对象至少有一个占位符。这会导致创建过多的空对象,因此已更改为提供在需要时创建的语义。

Specifically, my question is that could we replace final with volatile in JDK 1.6's implementation?

当然,只要操作继续保持顺序一致,它们就是这样。


Doug Lea 的评论之一涉及此设计更改

 /* 
 * ...
 * Historical note: The previous version of this class relied
 * heavily on "final" fields, which avoided some volatile reads at
 * the expense of a large initial footprint.  Some remnants of
 * that design (including forced construction of segment 0) exist
 * to ensure serialization compatibility. 
 */

因此,为了回答您可能遇到的另一个问题,为什么最初选择了 final?为了防止以后出现一些易失性读取。