集合是编程中最经常使用的数据结构。而谈到并发,几乎老是离不开集合这类高级数据结构的支持。好比两个线程须要同时访问一个中间临界区(Queue),好比常会用缓存做为外部文件的副本(HashMap)。这篇文章主要分析jdk1.5的3种并发集合类型(concurrent,copyonright,queue)中的ConcurrentHashMap,让咱们从原理上细致的了解它们,可以让咱们在深度项目开发中获益非浅。html
在tiger以前,咱们使用得最多的数据结构之一就是HashMap和Hashtable。你们都知道,HashMap中未进行同步考虑,而Hashtable则使用了synchronized,带来的直接影响就是可选择,咱们能够在单线程时使用HashMap提升效率,而多线程时用Hashtable来保证安全。编程
当咱们享受着jdk带来的便利时一样承受它带来的不幸恶果。经过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,安全的背后是巨大的浪费,慧眼独具的Doug Lee立马拿出了解决方案----ConcurrentHashMap。缓存
ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度以及如何锁。如图安全
左边即是Hashtable的实现方式---锁整个hash表;而右边则是ConcurrentHashMap的实现方式---锁桶(或段)。ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等经常使用操做只锁当前须要用到的桶。试想,原来只能一个线程进入,如今却能同时16个写线程进入(写线程才须要锁定,而读线程几乎不受限制,以后会提到),并发性的提高是显而易见的。数据结构
更使人惊讶的是ConcurrentHashMap的读取并发,由于在读取的大多数时候都没有用到锁定,因此读取操做几乎是彻底的并发操做,而写操做锁定的粒度又很是细,比起以前又更加快速(这一点在桶更多时表现得更明显些)。只有在求size等操做时才须要锁定整个表。而在迭代时,ConcurrentHashMap使用了不一样于传统集合的快速失败迭代器(见以前的文章《JAVA API备忘---集合》)的另外一种迭代方式,咱们称为弱一致迭代器。在这种迭代方式中,当iterator被建立后集合再发生改变就再也不是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可使用原来老的数据,而写线程也能够并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提高的关键。多线程