ConcurrentHashMap 如何在内部工作?

Posted

技术标签:

【中文标题】ConcurrentHashMap 如何在内部工作?【英文标题】:How does ConcurrentHashMap work internally? 【发布时间】:2012-08-01 08:17:30 【问题描述】:

我正在阅读有关 Java 并发的 Oracle 官方文档,我想知道 Collection 返回的 Collection 之间有什么区别

public static <T> Collection<T> synchronizedCollection(Collection<T> c);

并使用例如

ConcurrentHashMap。我假设我在HashMap 上使用synchronizedCollection(Collection&lt;T&gt; c)。我知道一般来说,同步集合本质上只是我的HashMap 的装饰器,所以很明显ConcurrentHashMap 在其内部有一些不同的东西。你有关于这些实施细节的一些信息吗?

编辑:我意识到源代码是公开的: ConcurrentHashMap.java

【问题讨论】:

stas-blogspot.blogspot.co.uk/2010/08/… amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601 恕我直言,这是每个 Java 开发人员都应该阅读的书之一 :-) 【参考方案1】:

这是帮助我理解它的文章Why ConcurrentHashMap is better than Hashtable and just as good as a HashMap

Hashtable 提供对其条目的并发访问,有一点需要注意的是,整个映射被锁定以执行任何类型的操作。 虽然这种开销在正常情况下的 Web 应用程序中是可以忽略的 负载,在重负载下会导致响应时间延迟和 无缘无故地过度使用您的服务器。

这就是 ConcurrentHashMap 介入的地方。它们提供所有功能 Hashtable 的性能几乎与 HashMap 一样好。 ConcurrentHashMap 通过一个非常简单的机制来完成这一点。 该集合维护了一个包含 16 个列表的列表,而不是地图范围的锁定 默认情况下有锁,每个都用于保护(或锁定)单个 地图的桶。这实际上意味着 16 个线程可以修改 一次收集(只要他们都在工作 不同的桶)。事实上,这并没有执行任何操作 锁定整个地图的集合。并发级别 集合,可以同时修改它的线程数 没有阻塞,可以增加。然而,更高的数字意味着更多 维护这个锁列表的开销。

【讨论】:

链接已失效,但it is still accessible it through the wayback machine @LaurentBourgault-Roy,欢呼编辑答案以反映这一点【参考方案2】:

ConcurrentHashMap 实现了提供并发的 ConcurrentMap。 在内部深处,它的迭代器设计为一次仅由一个线程使用,以保持同步。 此映射在并发中广泛使用。

【讨论】:

【参考方案3】:

Hashtable 的“可扩展性问题”在 Collections.synchronizedMap(Map) 中以完全相同的方式存在 - 它们使用非常简单的同步,这意味着只有一个线程可以同时访问地图。

当您进行简单的插入和查找时,这不是什么大问题(除非您非常密集地执行此操作),但是当您需要遍历整个 Map 时,这会成为一个大问题,这可能需要很长时间Map - 当一个线程执行此操作时,所有其他线程如果想要插入或查找任何内容,则必须等待。

ConcurrentHashMap 使用非常复杂的技术来减少同步需求,并允许多个线程在不同步的情况下进行并行读取访问,更重要的是,它提供了一个不需要同步的 Iterator,甚至允许在交互期间修改 Map (虽然它不保证在迭代期间插入的元素是否会被返回)。

【讨论】:

【参考方案4】:

synchronizedCollection()返回的是一个对象,其所有方法都在this上同步,所以在这个包装器上的所有并发操作都是序列化的。 ConcurrentHashMap 是一个真正的并发容器,具有优化的细粒度锁定以尽可能降低争用。看看源码就知道里面是什么了。

【讨论】:

【参考方案5】:

ConcurrentHashMapjava.util.HashTable 类非常相似,除了ConcurrentHashMap 提供比HashTablesynchronizedMap 更好的并发性。 ConcurrentHashMap 在您阅读时不会锁定地图。此外,ConcurrentHashMap 在写入时不会锁定整个Map。它仅在内部锁定正在写入的Map 部分。

另一个区别是,如果 ConcurrentHashMap 在迭代时发生更改,ConcurrentHashMap 不会抛出 ConcurrentModificationExceptionIterator 并非设计为供多个线程使用,而 synchronizedMap 可能会抛出 ConcurrentModificationException

【讨论】:

@PeterLawrey - 附议。投票赞成纠正 我也不明白他为什么对你投反对票。也投票给你。【参考方案6】:

我会阅读source of ConcurrentHashMap,因为它的细节相当复杂。简而言之,它有

可以独立锁定的多个分区。 (默认 16 个) 使用并发锁操作而不是同步来保证线程安全。 具有线程安全的迭代器。 synchronizedCollection 的迭代器不是线程安全的。 不暴露内部锁。 synchronizedCollection 可以。

【讨论】:

提供源链接会很有用 对于那些没有 JDK、IDE 或访问 google 的人我会添加一个链接。 ;) 我刚刚意识到源代码可供我使用。我看到 CHM 有自己的 ReentrantLock 子类用于锁定。感谢您的信息! 是的,每个分区一个 Segment。你可以增加分区的数量,但你很少需要(除了一些微基准;) 分区是指桶?或者这不是用经典的桶+链表模型实现的?

以上是关于ConcurrentHashMap 如何在内部工作?的主要内容,如果未能解决你的问题,请参考以下文章

SqlDataAdapter 如何在内部工作?

PHP 的“未设置”构造如何在内部工作?

getObject 函数在内部如何工作?

哈希:它在内部是如何工作的?

分段错误如何在内部(内核/硬件)工作?

Java 分析器在内部是如何工作的?