ConcurrentHashMap 中的 Dose Segment 存在虚假共享问题?

Dose Segment in ConcurrentHashMap has false sharing problems?

java.util.concurrent.ConcurrentHashMap 使用 Segment 数组作为 Mutex 并且 Segment Object 比缓存行小。

这会导致虚假分享吗?

虽然段很小,但这不是多线程支持发生的地方。它扩展了 ReentrantLock,后者使用 Sync 对象,而 Sync 对象扩展了使用 Node 对象的 AbstractQueuedSchronizer。所有这些对象都将作为一个组进行复制,并且可能会合理地间隔开。这将取决于 GC 的实现方式,但对于 Hotspot,它是按发现的相反顺序复制的(即深层复制),因此这可能没问题。

首先,最好区分Java 7中的ConcurrentHashMap和Java 8中的ConcurrentHashMap,因为实现差异很大。

ConcurrentHashMap 在 Java 7

Peter Lawrey 是正确的,Segment class 扩展了 ReentrantLock,它聚合了 AbstractQueueSynchronizer 的专业化。 AbstractQueuedSynchronizer 本身包含足够的 long 字段来填充缓存行的通常填充长度(64 字节)。如果你算一下,可能会发现 Segment 实际上需要 1 个缓存行 + 一点点,所以你可能会争辩说它容易受到错误共享的影响。 Java Object Layout and Intel VTune 应该可以帮你解决这个问题。

ConcurrentHashMap 在 Java 8

在Java8中,ConcurrentHashMap不再依赖条带锁定。相反,它使用的是一种基本无锁的算法,该算法适用于桶的粒度级别(在新实现中称为 Node),而不是一组桶(在旧实现中称为 Segment)。如果您深入了解实施细节,您会注意到:

  1. 新实现不太容易受到错误共享的影响,因为您希望不同的线程仅在发生存储桶冲突时才会命中相同的 Node
  2. @Contended 注释用于 Java 8 的 ConcurrentHashMap 的内部实现,因此 Doug Lea 和其他从事此工作的人确实一直在考虑虚假共享。它恰好用在你期望错误共享最严重的地方 - 当你试图跟踪 table.
  3. 的大小时