正如我们所知,java 的 ConcurrentHashMap 有许多内部锁,每个锁都守卫着桶数组的某个区域。
一个问题是:为什么我们不能为每个桶创建一个锁?
已经有人问过类似的问题:Disadvantage of increasing number of partition in Java ConcurrentHashMap?
根据回答,有以下几个原因:
同时运行的最大线程数受处理器内核数的限制。 这是正确的吗? 我们能否始终声明如果我们有 8 核处理器,我们在 ConcurrentHashMap 中不需要超过 8 个锁定区域?
存在二级缓存的浪费。 为什么?
存在内存浪费。看起来这是因为创建了额外的锁。
还有其他原因吗?
最佳答案
希望我能好好解释一下……现在有点仓促……
第一个问题的答案:
"why cannot we create a lock for each bucket?"
您是否可以为每个存储桶创建一个锁 - 这不一定是最好的做法。
问题的答案:
"Can we ALWAYS state that if we have 8-core processor we do not need more than 8 locked regions in ConcurrentHashMap"
在技术上是“否”,尽管这取决于您所说的“需要”是什么意思。拥有多个与您系统的最大并发性相匹配或稍大的区域并不一定能防止争用,但在实践中它工作得很好。没有什么可以阻止两个线程同时尝试访问同一区域,即使还有其他未锁定的区域。
通过在 8 核处理器上拥有 8 个或更多区域,您可以保证可以同时访问所有区域而不会发生争用。如果你有 8 个核心(不是超线程),你最多可以同时执行 8 个操作。即便如此,理想的区域数量(例如 16 个)也可能比核心数量更多(例如 16 个),因为它会以较低的成本(仅 8 个额外的锁)降低争用的可能性。
随着区域数量相对于最大并发数的增加,拥有额外区域的好处最终会减少,这会导致它们浪费空间(内存),如 JavaDoc 中所述。 .它是争用可能性(给定一个区域的锁定,另一个线程尝试访问它的可能性是多少)和空间浪费之间的平衡。
还有一些其他因素会影响 ConcurrentHashMap
的性能:
- 锁定代码的执行时间 - 最好使锁定代码部分较小,以便它们快速完成并释放锁定。释放锁的速度越快,解决争用的速度就越快。
- 数据分布 - 良好分布的数据往往在高并发情况下表现更好。将所有数据集中在一个区域内意味着您将始终遇到争用。
- 数据访问模式 - 同时访问不同的数据区域会表现得更好,因为您的线程不会争用资源锁。如果您一次只尝试访问一个区域,那么拥有良好分布的数据并不重要。
无论有多少个区域,所有这三件事都会对性能产生积极或消极的影响,并且会降低区域数量的相关性。由于它们发挥了重要作用,因此它们不太可能拥有更多的区域来帮助您。由于您只能同时执行这么多线程,因此拥有能够快速完成工作并释放锁的线程是更好的重点。
关于您关于缓存的问题:老实说我不确定,但我可以猜测一下。当您大量使用 map 时,这些锁最终会进入缓存并占用空间,可能会撞出其他可能更有用的东西。缓存比主存稀缺得多,缓存未命中会浪费很多时间。我认为这里的想法是普遍厌恶将许多不会提供显着好处的东西放在缓存中。极端一点:如果缓存中充满了锁(不知何故)并且每个数据调用都进入了内存,那么您的性能就会受到影响。
关于java - 为什么 ConcurrentHashMap 不能为每个桶都加锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25411087/