• 令我困惑的是这个。

  • ConcurrentHashMap 中 HashEntry 的 Java 文档 (jdk1.6.0_16)

    ...因为 value 字段是可变的,而不是最终的,所以在通过数据竞争读取时,对于未同步的读取器来说,Java 内存模型看到 null 而不是初始值是合法的。虽然导致这种情况的重新排序不太可能实际发生,但 Segment.readValueUnderLock 方法用作备份,以防在未同步的访问方法中看到空(预初始化)值。

    这里的
  • 是ConcurrentHashMap#Segment的get方法的实现
    
    V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry e = getFirst(hash);
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.value;
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck
                    }
                    e = e.next;
                }
            }
            return null;
        }
    
  • 和 readValueUnderLock
  • 
    V readValueUnderLock(HashEntry e) {
            lock();
            try {
                return e.value;
            } finally {
                unlock();
            }
        }
    
  • 根据我的阅读和理解,每个线程都会读取 volatile 变量的最新值。
  • 那么线程什么时候读取初始空值呢?特别是在 HashEntry 中,在构造函数完成之前赋值。 (另请注意,HashEntry 的引用永远不会转义其构造函数。)
  • 我很难过,有人可以解释一下ConcurrentHashMap(jdk1.6.0_16)中HashEntry的上述java文档。为什么需要额外的预防性锁定?
  • 最佳答案

    在 Java 1.5 发布时,JMM 中有一个规定说 HashEntry 可以部分初始化。也就是说,当一个线程放入映射时,HashEntry 被创建并分配为对桶头或 collison 成员的引用。当时条目的值,可能还没有被分配给其他线程看到。

    CHM 假定如果条目不为空,则该值不应为空,因此 readValueUnderLock 被放入作为故障保护。

    我向 DL 询问了这种确切情况,他说尽管有可能发生,但绝不应该发生。他还说,从 1.6 开始,这个问题就不会发生了。

    10-06 06:46