ConcurrentHashMap 内部是如何工作的?

56

我正在阅读官方的Oracle Java并发文档,我想知道由Collection返回的内容有什么区别。


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

假设我使用ConcurrentHashMap, 例如使用synchronizedCollection(Collection<T> c) 在一个HashMap上。我知道通常情况下,同步集合本质上只是我的HashMap的装饰器,因此很明显ConcurrentHashMap在其内部有所不同。你是否有一些关于这些实现细节的信息?

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


1
http://stas-blogspot.blogspot.co.uk/2010/08/concurrenthashmap-revealed.html - Thilo
3
我认为这是每个Java开发者都应该阅读的书籍 :-) 请访问http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601。 - Boris Treukhov
6个回答

52

我建议阅读ConcurrentHashMap的源代码,因为它在细节方面相当复杂。简而言之,它具有以下特点:

  • 可以独立锁定多个分区(默认16个)。
  • 使用并发锁操作实现线程安全,而不是使用synchronized关键字。
  • 具有线程安全的迭代器。synchronizedCollection的迭代器不是线程安全的。
  • 不暴露内部锁。synchronizedCollection则反之。

提供源链接会很有用。 - Brian Agnew
16
如果没有JDK、IDE或无法访问谷歌,我将添加一个链接。;) - Peter Lawrey
我刚刚意识到源代码对我是可用的。我看到CHM有自己的ReentrantLock子类来进行锁定。感谢您提供的信息! - Adam Arold
是的,每个分区一个段。你可以增加分区的数量,但通常不需要这样做(除了一些微基准测试;) - Peter Lawrey
你所说的“partition”是指桶吗?还是说这不是采用经典的桶+链表模型实现的? - Adam Arold
顺便说一句,ConcurrentHashMapV8+ 是一个相当大的改进,可以在 http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?view=markup 上获取。 - bestsss

28

ConcurrentHashMap类与java.util.HashTable类非常相似,但是ConcurrentHashMapHashTablesynchronizedMap提供了更好的并发性。在读取Map时,ConcurrentHashMap不会锁定它。此外,在向Map写入数据时,ConcurrentHashMap只会在内部锁定正在写入的那一部分Map,而不是整个Map

另一个区别是,如果在迭代ConcurrentHashMap时对其进行更改,它不会抛出ConcurrentModificationException异常。但是请注意,虽然synchronizedMap也可以使用Iterator,但如果在迭代时修改synchronizedMap,则可能会抛出ConcurrentModificationException异常。


@PeterLawrey - 赞同。投票支持纠正。 - Brian Agnew
我也不明白他为什么要给你点踩。我也给你点赞了。 - Adam Arold

18

这篇文章帮助我理解了为什么ConcurrentHashMap比Hashtable更好,而且与HashMap一样好

Hashtable提供对其条目的并发访问,但是有一个小问题,整个映射在执行任何操作时都会被锁定。在正常负载下,这种开销可以忽略不计,但在重负载下,它可能会导致响应时间延迟,并且无端地过度使用服务器资源。
这就是ConcurrentHashMap的作用。它们提供了Hashtable的所有功能,性能几乎与HashMap一样好。ConcurrentHashMap通过一种非常简单的机制实现了这一点。该集合默认维护16个锁的列表,每个锁都用于保护(或锁定)映射的单个桶。这有效地意味着16个线程可以同时修改集合(只要它们都在处理不同的桶)。实际上,此集合执行的任何操作都不会锁定整个映射。集合的并发级别(可以同时修改它而不阻塞的线程数)可以增加。但是更高的数字意味着需要维护此锁列表的更多开销。

4
链接已失效,但可以通过Wayback Machine访问它,该链接解释了为什么ConcurrentHashMap比Hashtable更好,并且和HashMap一样好用。 - Laurent Bourgault-Roy
@LaurentBourgault-Roy,感谢您编辑答案以反映这一点。 - reevesy

5
Hashtable的“可伸缩性问题”在Collections.synchronizedMap(Map)中也存在,它们使用非常简单的同步方式,这意味着同一时间只能有一个线程访问map。当您执行简单插入和查找时,这不是什么问题(除非您非常密集地执行),但是当您需要遍历整个Map时,这将成为一个大问题 - 当一个线程进行此操作时,如果其他线程想要插入或查找任何内容,则必须等待。ConcurrentHashMap使用非常复杂的技术来减少同步需求,并允许多个线程并行读取而无需同步,更重要的是,提供了一个迭代器,该迭代器不需要同步,甚至允许在迭代期间修改Map(虽然它不能保证在迭代期间插入的元素是否会被返回)。

4

synchronizedCollection() 返回的是一个对象,所有方法都在 this 上进行同步,因此对该包装器的所有并发操作都是串行化的。ConcurrentHashMap 是一个真正的并发容器,具有细粒度的锁定优化,以使争用尽可能少。查看源代码,您将看到其中的内容。


-2

ConcurrentHashMap实现了ConcurrentMap,提供并发性。 在内部,它的迭代器被设计为一次只能由一个线程使用,以维护同步。 这个映射在并发中被广泛使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接