为什么ConcurrentHashMap中的get方法是阻塞的?

4

我对Java中的ConcurrentHashMap有一个问题。它在内部调用readValueUnderLock。为什么在进行get操作时需要锁定。在哪种情况下会出现下列条件为真的情况(Entry.value==null),导致调用readValueUnderLock。

2个回答

4

readValueUnderLock方法的Java源代码文档注释:

  /**
     * Reads value field of an entry under lock. Called if value
     * field ever appears to be null. This is possible only if a
     * compiler happens to reorder a HashEntry initialization with
     * its table assignment, which is legal under memory model
     * but is not known to ever occur.
     */

从这个链接中可以了解到:

不完全正确。你是对的,它永远不应该被调用。 然而,JLS/JMM可以被理解为并未绝对禁止其被调用, 因为构造函数中所需的finals和volatiles之间的顺序关系存在弱点 (key是final,value是volatile),对于使用entry对象读取的线程来说, 这就涉及到了问题。(在JMM-ese中,finals的排序限制超出了同步关系。) 这就是文档注释(如下所示)所指的问题。 没有人想到过任何实际漏洞,即处理器/编译器可能找到的产生空值读取的漏洞, 可能可以证明不存在这样的漏洞(也许有一天JLS/JMM的修订将填补这些空白以澄清这一点), 但Bill Pugh曾建议我们把这个放进去,只是为了保守地严谨正确。 回想起来,我不太确定这是一个好主意,因为它会导致人们提出奇特的理论。


但是我不确定HashEntry的初始化如何与其表分配重新排序,我想了解在什么情况下键不会为null,而值可能为null。从Java文档中看,这似乎从未发生过,那么为什么要包括此方法进行锁定(因为HashEntry实际上是不可变的,除了值之外,所有字段都是final的,而值则是volatile的)? - tarunk
@tarunk 从javadoc中看来,它是针对一个特殊情况编写的。 - Amit Deshpande
有没有想法针对那种特殊情况,它是如何发生的,因为jsr 133保证了final/volatile变量的排序。 - tarunk
谢谢Amit,但我仍然有一些疑问,因为在构造函数中有一些初始化保证(通过确保在构造过程中不会逃逸),这样在对象被构造时没有其他线程可以读取它。 - tarunk

1
为了从哈希映射中读取一个值,代码必须先找到这个值。如果另一个线程在第一个线程查找该值时添加一个值,它可能会破坏这个搜索过程。实质上,哈希映射可能会执行以下操作:
calculate hash
go to location hash in the array
look to see if there's a list
iterate through the list until value is found

如果这个列表是一个数组列表,并且另一个线程需要调整它的大小,那么这将对迭代它的线程造成很大的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接