ConcurrentHashMap中的读操作关于返回值是否可靠?

我在一本书中读到,在ConcurrentHashmap中读取并不保证最近更新的状态,它有时会给出更接近的值。 它是否正确?

我已经阅读了它的javadocs和许多博客,似乎在另外说(即它是准确的)。

哪一个是真的?

直觉上,ConcurrentHashMap的行为应该像一组volatile变量; 映射键是变量地址。 get(key)put(key, value)应该像volatile读写一样。

这在文件中没有明确说明。 但是,我坚信这是事实。 否则将会出现许多意外的,令人惊讶的行为,这些行为会破坏应用程序逻辑。 我认为Doug Lea不会对我们这样做。 可以肯定的是,有人请在concurrency-interest邮件列表上询问他。

假设它确实遵守volatile语义,我们可以基于Java Memory Model进行推理 –

所有易失性读写都构成一个总订单。 这可以被认为是伪时间线,其中读/写是其上的点。

易失性读取会看到前一个易失性写入,并且只能看到该写入。 这里的“前置”是根据伪时间线。

伪时间线可以与“实际”时间线不同。 然而,理论上,不能在伪时间线上无限延迟易失性写入。 而且,在实践中,两个时间线非常接近。

因此,我们可以非常肯定,易读写应该“非常快”地显示为读取。

这就是并发的本质。 如果当前挂起的写入操作未安全完成,则任何并发集合都可能在字段中提供旧值。 在所有情况下,这都是您所期望的。 为并发而创建的集合不会为您提供损坏的值,如果您从多个线程访问它们会中断,但如果没有写入,它们可能会为您提供旧值。

来自ConcurrentHashMap javadoc :

检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。 检索反映了最近完成的更新操作的结果。 对于诸如putAll和clear之类的聚合操作,并发检索可能反映仅插入或删除某些条目。 类似地,Iterators和Enumerations在迭代器/枚举的创建时或之后的某个时刻返回反映哈希表状态的元素。 它们不会抛出ConcurrentModificationException。 但是,迭代器设计为一次只能由一个线程使用。

需要强调的一点是, Iterator不会反映创建Iterator后所做的更改。 因此,如果您需要迭代地图的值,而其他线程正在添加到地图中,并且您关心地图的最新状态,那么使用ConcurrentHashMap可能不是您需要的地图的实现。