28

我正在尝试找到这些问题的答案,但无法在 Google 或 Java 文档中找到。

情况 1:在 中ConcurrentHashMap,假设线程t1正在从段n读取,同时另一个线程t2想要在同一段 n上写入:

问题一:这两个操作是一个接一个,还是同时执行?


情况 2:在 中ConcurrentHashMap,假设线程t1正在段n上写入,同时另一个线程t2想要从同一段 n中读取,

问题二:这两个操作是一个接一个,还是同时执行?

4

2 回答 2

30

我认为 javadoc 回答了你的两个问题:

检索操作(包括 get)一般不会阻塞,因此可能与更新操作(包括 put 和 remove)重叠。检索反映了最近完成的更新操作在其开始时保持的结果。对于 putAll 和 clear 等聚合操作,并发检索可能仅反映插入或删除某些条目。

段用于更新操作:

更新操作之间允许的并发性由可选的 concurrencyLevel 构造函数参数(默认为 16)指导,该参数用作内部大小调整的提示。

因此,简而言之,读取不会被阻塞(它被实现为读取 volatile 变量)。如果在同一段中写入,则写入可能会相互阻塞。

于 2013-04-19T14:31:49.267 回答
4

根据 ConcurrentHashMap Oracle 文档,

ConcurrentHashMap 的构造函数如下所示:

公共 ConcurrentHashMap (int initialCapacity, float loadFactor, int concurrencyLevel)

因此,上面的行创建了一个具有指定初始容量、负载因子和并发级别的新的空映射。其中,要从 ConcurrentHashMap 构造函数中考虑的重要参数:

  • initialCapacity - 初始容量。该实现执行内部大小调整以适应这么多元素。
  • concurrencyLevel - 估计的并发更新线程数。该实现执行内部大小调整以尝试容纳这么多线程。

在 ConcurrentHashMap Api 中,您将找到以下常量。

  • 静态最终 int DEFAULT_INITIAL_CAPACITY = 16;
  • 静态最终 int DEFAULT_CONCURRENCY_LEVEL = 16;

ConcurrentHashMap构造函数(或Object)的初始容量参数和并发级别参数默认设置为16。

因此,默认情况下,ConcurrentHashMap 维护了一个包含 16 个锁的列表(锁的数量等于初始容量,默认为 16),而不是 map 范围的锁,每个锁用于锁定 Map 的单个存储桶。这表示16个线程(线程数等于并发级别,默认为16个)可以同时修改集合,假设每个线程工作在不同的桶上。因此,与哈希表不同,我们执行任何类型的操作(更新、删除、读取、创建)而不锁定 ConcurrentHashMap 中的整个映射。

检索操作(包括 get)一般不会阻塞。在这种情况下,它使用了 volatile 的概念。,因此可能与更新操作(包括 put 和 remove)重叠。检索反映了最近完成的更新操作在其开始时保持的结果。

更新操作之间允许的并发性由可选的 concurrencyLevel 构造函数参数(默认为 16)指导,该参数用作内部大小调整的提示。该表在内部进行了分区,以尝试允许指定数量的并发更新而不会发生争用。因为哈希表中的放置本质上是随机的,所以实际的并发性会有所不同。理想情况下,您应该选择一个值来容纳尽可能多的线程同时修改表。使用显着高于您需要的值会浪费空间和时间,而显着降低的值会导致线程争用。

我希望它有帮助!

于 2017-09-26T11:25:38.750 回答