我正在阅读《Java concurrency in practice》这本书。在第85页的5.2.1节中讨论了ConcurrentHashMap及其优点。然而,书中有一部分声称
ConcurrentHashMap返回的迭代器弱一致。这意味着这个迭代器可以容忍并发修改,遍历构造迭代器时存在的元素,和可以(但不保证)反映对集合的修改在构造迭代器之后。
从我理解并发程序中同步的全部要点是允许线程以一致的方式访问共享资源,而ConcurrentHashMap并没有真正实现这一点。那为什么要用它呢?
关键是在不需要同步时避免同步。如果您不介意在某些情况下看到新元素而在其他情况下看不到它们,使用ConcurrentHashMap
的迭代器可以比便宜得多,或者在您迭代时防止其他线程添加项,或者在创建迭代器时获取一致的快照。
所以,是的,当你需要同步和一致迭代器时,你需要一个替代方案——但是当你不需要时,你可以利用ConcurrentHashMap
提供的更有效的迭代器。
根据合同,ConcurrentHashMap
应该是线程安全的。然而,它不应该是跨线程一致的。
当您在ConcurrentHashMap
上迭代时,迭代器在您请求它的那一刻获取哈希映射的副本(并且此副本是以线程安全的方式进行的),并且您迭代该副本。是的,没有什么能保证当你在复制上迭代时,一些映射条目不会被删除。但是以这种方式删除的映射项仍然存在于迭代器中。
一切都取决于你需要的一致性有多强。如果您需要强互斥锁,那么互斥锁或其他操作子集可以满足您的需要。
然而,有时你需要更弱的要求,但例如,你需要说无锁属性,因为你需要保证吞吐量。另一方面,您可能根本不需要迭代器,或者对它们的限制要弱得多。因此,如果您需要的是跨线程共享的映射,ConcurrentHashMap
(例如剧院中的位置到预订它的人)将提供您所需要的-如果您有许多工作线程,它可能会更快,因为您避免了互斥锁同步。另一方面,速度的提高是有代价的——迭代器提供的视图不一定与创建时的集合状态相对应,并且可能会遗漏一些后续创建的元素(通常在多处理器系统中happens-before
关系很棘手)。
编辑:正如John Wesley王子指出的,我在看和写的时候想到了ConcurrentSkipListMap
。ConcurrentHashMap
。除了关于无锁的部分(以及由此产生的所有内容,如保证吞吐量等)之外,大多数观点仍然存在。