java.util.concurrent.ConcurrentHashMap
使用Segment
数组作为Mutex
并且Segment Object
比缓存线小。
这会导致虚假分享吗?
java.util.concurrent.ConcurrentHashMap
使用Segment
数组作为Mutex
并且Segment Object
比缓存线小。
这会导致虚假分享吗?
首先,最好区分ConcurrentHashMap
Java 7 和ConcurrentHashMap
Java 8,因为实现差异很大。
ConcurrentHashMap
在 Java 7 中
Peter Lawrey 是正确的,Segment
类 extendsReentrantLock
聚合了AbstractQueueSynchronizer
. 就其本身而言,AbstractQueuedSynchronizer
包含足够的long
字段来填充缓存行的通常填充长度(64 字节)。如果您进行数学计算,可能会发现Segment
实际上需要 1 个缓存行 + 一点点,因此您可能会争辩说它容易受到错误共享的影响。
Java Object Layout和Intel VTune应该能够帮助您解决这个问题。
ConcurrentHashMap
在 Java 8 中
在 Java 8 中,ConcurrentHashMap
不再依赖条带锁定。相反,它使用了一种大部分无锁算法,该算法适用于存储桶的粒度级别(Node
在新实现中调用),而不是一组存储桶(Segment
在旧实现中调用)。如果您深入研究实现细节,您会注意到:
Node
只有在桶冲突时才会命中相同的线程。@Contended
注解用于 Java 8 的内部实现ConcurrentHashMap
,因此 Doug Lea 和其他研究它的人确实一直在考虑虚假共享。它恰好用于您期望虚假共享最严重的地方 - 当您尝试跟踪表格的大小时。虽然段很小,但这不是多线程支持发生的地方。它扩展了 ReentrantLock,它使用了一个 Sync 对象,该对象扩展了 AbstractQueuedSchronizer,它使用了 Node 对象。所有这些对象都将作为一个组进行复制,并且可能会被合理地隔开。这将取决于 GC 的实现如何完成,但对于 Hotspot,它是以发现的相反顺序复制的(即深拷贝),所以这可能会很好。