[size=small]
同步容器类在执行每个操作期间都持有一个锁。在一些操作中,例如 HashMap.get 或List.contains,
可能包含大量的工作;当遍历散列桶或链表来查找某个特定的对象时,必须在许多元素上调用equals(equals本身还包含一定的计算量)。
在基于散列的容器中,某些情况下,某个糟糕的散列函数还会把散列表变成现行链表。当遍历很长的链表并且在某些或者全部元素上调用equals方法时,
会话费很长的时间,而其他线程在这段时间内都不能访问该容器。
与HashMap 一样,ConcurrentHashMap 也是一个基于散列的Map,但他使用了完全不同的加锁策略来提供更高的并发
性和伸缩性
ConcurrentHashMap 并不是将每个方法都在同一个锁上同步并使得每一次只能有一个线程访问容器,而是使用一种力
度更细的加锁机制来实现更大成都的共享,
这种机制称为分段锁(Lock Striping),在这种机制中国,任意数量的读取线程可以并发的访问Map,执行读取操作的
线程和执行写入操作的线程可以并发的访问Map,
并且一定数量的写入线程可以并发的修改Map。ConcurrentHashMap 带来的结果是,在并发访问环境下将实现更高
的吞吐量,二期单线程环境中只损失非常小的性能。
ConcurrentHashMap 与其他并发容器一起增强了同步容器类:他们提供的迭代器不会抛出ConcurrentModificationException ,
因此不需要在迭代的过程中对容器加锁。 COncurrentHashMap 返回的迭代器具有弱一致性,而并非"及时失败"。
弱一致性的迭代器可以容忍并发的修改,当创建迭代器时会遍历已有的元素,并可以在迭代器被构造之后将修改操作反映给容器。
尽管有这些改进,但仍然有一些需要权衡的因素。对于一些需要在整个Map上进行计算的方法,例如size 和isEmpty,
这些方法的语义被略微减弱了以反映容器的并发特性。由于size返回的结果在计算时可能已经过期了,他实际上只是一个估值,因此允许size返回
一个近似值而不是一个精确值。这虽然看上去让人不安,但实际上size和isEmpty这样的方法在并发环境下用的很少,因为他们返回的值总在不断变化,因此,
这些操作的需求被弱化了,以换取其他更重要操作的性能优化。包括get/put/containsKey/remove等。
在ConcurrentHashMap 中没有实现对Map加锁以提供独占访问,在hashtable 和synchronizedMap 中,获取Map的锁能防止其他线程访问这个Map。
在一些不常见的情况下需要这种功能,例如通过原子方式添加一些映射,或者对Map迭代若干次并在此期间保持元素顺序相同。然而,总体来说这种权衡还是合理的
因为并发容器的内容会持续变化。
与Hashtable 和 synchronizedMap 相比,ConcurrentMap 有这更多的优势和更少的劣势,因此在大多数情况下,用ConcurrentHashMap来代替Map
能进一步提高代码的可伸缩性。只有当应用程序需要加锁Map以独占访问时,才应该放弃使用ConcurrentHashMap.
[/size]