- 让我困惑的是这个。
ConcurrentHashMap中HashEntry的Java文档(jdk1.6.0_16)
...因为值字段是易失性的,而不是最终的,所以在 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 doc。为什么需要额外的预防锁定?