ConcurrentHashMap 中 HashEntry 的 Java 文档 (jdk1.6.0_16)
...因为 value 字段是可变的,而不是最终的,所以在通过数据竞争读取时,对于未同步的读取器来说,Java 内存模型看到 null 而不是初始值是合法的。虽然导致这种情况的重新排序不太可能实际发生,但 Segment.readValueUnderLock 方法用作备份,以防在未同步的访问方法中看到空(预初始化)值。
这里的
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;
}
V readValueUnderLock(HashEntry e) {
lock();
try {
return e.value;
} finally {
unlock();
}
}
最佳答案
在 Java 1.5 发布时,JMM 中有一个规定说 HashEntry 可以部分初始化。也就是说,当一个线程放入映射时,HashEntry 被创建并分配为对桶头或 collison 成员的引用。当时条目的值,可能还没有被分配给其他线程看到。
CHM 假定如果条目不为空,则该值不应为空,因此 readValueUnderLock 被放入作为故障保护。
我向 DL 询问了这种确切情况,他说尽管有可能发生,但绝不应该发生。他还说,从 1.6 开始,这个问题就不会发生了。