8

Java ConcurrentHashMap 在内部维护分区。每个分区可以单独锁定。可能存在多个线程访问的所有键都落入同一个分区并且分区可能没有帮助的情况。进一步增加分区的数量应该会提高并发性。

为什么 Java 将分区计数的默认值提供为 16 而不是非常高的值?Map 中的大量分区的性能如何?

4

2 回答 2

6

为什么 Java 将分区计数的默认值提供为 16 而不是非常高的值?

很少有这么多 CPU(线程数不是那么重要)同时使用同一个 CHM。如果你真的需要这个,通常有更好的方法来编写你的应用程序来避免这种情况。

例如,假设您有 1000 个线程,但只有 8 个 CPU。这意味着最多只有 8 个线程将运行并访问 CHM,假设您的程序没有做任何有用的事情,例如其他任何事情。

在实际程序中,很少有一个集合被使用超过 10% 的时间。这是因为通常涉及一些 IO,或者重组线程以使用自己的集合副本并在最后将它们收集在一起是有意义的,例如 Map-Reduce

Map 中的大量分区的性能如何?

您浪费了一点内存,这无关紧要,但主要是浪费了一些限制为 32 KB 的 L1 缓存和相对宝贵的资源。

于 2013-06-21T11:22:05.023 回答
2

这是javadoc所说的(Java 6):

“更新操作之间允许的并发性由可选的 concurrencyLevel 构造函数参数(默认为 16)指导,该参数用作内部大小调整的提示。表在内部进行分区以尝试允许指定数量的并发更新而不会发生争用。因为放置在哈希表中本质上是随机的,实际并发性会有所不同。理想情况下,您应该选择一个值来容纳尽可能多的线程同时修改表。使用比您需要的值高得多的值会浪费空间和时间,并且显着较低的值会导致线程争用。但是一个数量级内的高估和低估通常不会产生太大的影响。当知道只有一个线程会修改而所有其他线程只会读取时,值 1 是合适的。另外,调整这种或任何其他类型的哈希表的大小是一个相对较慢的操作,因此,如果可能,最好在构造函数中提供预期表大小的估计值。”

所以简短的回答是默认值(16)是限制并发和浪费空间之间的折衷。“非常高”的值会浪费大量空间。(正如 Peter Lawrey 指出的那样,由于内存缓存效应,这可能会导致性能下降。)

另一件需要注意的是,该实现默默地将2 16LinkedHashMap的值设置为上限。(至少,Java 6 代码就是这样做的。)很难想象一个实际场景中您需要这么多的并发性。concurrencyLevel

于 2013-06-21T11:30:41.480 回答