【问题标题】:Disadvantage of increasing number of partition in Java ConcurrentHashMap?Java ConcurrentHashMap 中增加分区数量的缺点?
【发布时间】:2013-06-21 11:16:37
【问题描述】:
Java ConcurrentHashMap 在内部维护分区。每个分区可以单独锁定。
在某些情况下,多个线程访问的所有键都落在同一个分区中,分区可能没有帮助。
进一步增加分区数量应该会提高并发性。
为什么 Java 为分区计数提供默认值 16 而不是非常高的值?
Map 中的大量分区的性能如何?
【问题讨论】:
标签:
java
data-structures
concurrenthashmap
【解决方案1】:
为什么 Java 提供分区计数的默认值是 16 而不是非常高的值?
很少有这么多 CPU(线程数不是那么重要)同时使用同一个 CHM。如果你真的需要这个,通常有更好的方法来编写你的应用程序来避免这种情况。
例如,假设您有 1000 个线程但只有 8 个 CPU。这意味着最多只有 8 个线程将运行并访问 CHM,假设您的程序没有做任何有用的事情,例如还有什么。
在实际程序中,很少有一个集合被使用超过 10% 的时间。这是因为通常涉及一些 IO,或者重组线程以使用自己的集合副本并在最后将它们收集在一起是有意义的,例如Map-Reduce
在 Map 中有大量分区时,性能如何?
你浪费了一点内存没关系,但主要是你浪费了一些限制在 32 KB 的 L1 缓存和相对宝贵的资源。
【解决方案2】:
这是 javadoc 所说的(Java 6):
"更新操作之间允许的并发由可选的 concurrencyLevel 构造函数参数(默认 16)引导,该参数用作内部大小调整的提示。表在内部分区以尝试允许指定的并发数更新而不会发生争用。因为哈希表中的放置本质上是随机的,所以实际的并发性会有所不同。理想情况下,您应该选择一个值来容纳尽可能多的线程同时修改表。使用比您需要的值高得多的值会浪费空间和时间,并且显着降低的值会导致线程争用。但是一个数量级内的高估和低估通常不会产生太大的影响。当知道只有一个线程会修改而所有其他线程会修改时,值 1 是合适的只会读取。此外,调整这种或任何其他类型的哈希表的大小是一个相对较慢的操作,因此,如果可能,最好提供 e 的估计值构造函数中的预期表大小。"
所以简短的回答是默认值 (16) 是限制并发和浪费空间之间的折衷。 “非常高”的值会浪费大量空间。 (正如 Peter Lawrey 指出的那样,由于内存缓存效应,这可能会导致性能下降。)
要注意的另一件事是LinkedHashMap 实现默默地将concurrencyLevel 的值设置为216。 (至少,Java 6 代码就是这样做的。)很难想象一个实际场景中您需要那么多 并发性。