java - ConcurrentHashMap 及其操作

标签 java collections thread-safety

假设有一个ConcurrentHashMap并且有两个线程。

如果两个线程都从同一个存储桶中读取一些数据,那么我的理解是,两个线程都可以同时读取该存储桶,因为 CHM 不会阻止读取操作。

但是假设一个线程正在向存储桶写入 (put)。那么,第二个线程是否可以同时从同一个存储桶中读取 (get),还是必须等待 put 操作完成?

如果是Hashtable,那么get将必须等待put操作完成。但如果是 CHM,它会如何表现?

最佳答案

没有必要猜测。 source code for ConcurrentHashMap是开放的,任何人都可以阅读。 (这是 JDK 8 build 128,第一个 JDK 8 候选版本。)

您应该可以轻松理解它,因为它只有 6,300 行长。 :-) 实际上,其中很大一部分是注释,并且大部分代码用于处理边缘情况。 get() 和 put() 的简单路径并不是非常复杂,只有几十行代码。

您对读操作(get()、contains())的理解是正确的;没有阻塞。散列到存储桶并在必要时在存储桶内进行搜索非常简单,无需锁定。内存可见性通过 volatile 读取来确保。 (在第 622-623 行,Nodevalnext 字段是 volatile 的。)读取操作与其他读取以及写入同时进行到同一个桶。

删除和替换值的策略相当简单,因为在搜索和修改存储桶时,存储桶的头部被锁定。请参阅 replaceNode 第 1117 行的 synchronized block 。添加到现有存储桶的 put 类似;请参阅 putVal 第 1027 行的 synchronized block 。这些操作当然会阻止其他线程尝试删除、替换或添加条目到同一存储桶。如果某个值正在被替换,则获取该键的值的线程将看到旧值或新值,具体取决于读取线程是在该值被替换之前还是之后找到该节点。编写线程。

有一种特殊情况,可以将第一个元素放入存储桶中。在第 1018-1020 行,如果 putVal 发现存储桶为空,它将创建一个新节点并将其 CAS(比较并交换)到位。如果成功,则操作完成。如果两个线程尝试或多或少同时将节点添加到同一个存储桶中,则第一个线程的 CAS 将成功,而第二个线程的 CAS 将失败。但请注意,此代码位于 for 循环内(第 1014 行)。 CAS 失败的线程只是简单地循环并重试。事实上,所有其他写操作都在循环内。一般方法是操作乐观地进行,但检查并发写入者。如果乐观尝试失败,则会重试该操作,并根据现在更新的状态通过(可能)不同的路径。

关于java - ConcurrentHashMap 及其操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21600289/

相关文章:

java - 针对多个谓词扫描一次数组或针对单个谓词多次扫描数组是否更有效

java - Eclipse插件: Get the class name on user's click/selection

java - 将对象添加到 ArrayList 而不引用

c# - IEnumerable 和顺序

c++ - 同时从 2 个线程读取全局变量

java - JSON 响应中途中断

java - 尝试使用 toString 绘制坐标

java - 为什么List可以移除一个没有实例化的新对象

c# - 在运行内部代码之前,在锁之前和锁内设置 "double check"是否可以?

java - Hibernate:清理 session