##Map体系## Hashtable是JDK 5以前Map惟一线程安全的内置实现(Collections.synchronizedMap不算)。Hashtable继承的是Dictionary(Hashtable是其惟一公开的子类),并不继承AbstractMap或者HashMap.尽管Hashtable和HashMap的结构很是相似,可是他们之间并无多大联系。 ConcurrentHashMap是HashMap的线程安全版本,ConcurrentSkipListMap是TreeMap的线程安全版本。 最终可用的线程安全版本Map实现是ConcurrentHashMap、ConcurrentSkipListMap、Hashtable、Properties四个,可是Hashtable是过期的类库,所以若是能够的应该尽量的使用ConcurrentHashMap和ConcurrentSkipListMap.html
##简介## ConcurrentHashMap 是 java.util.concurrent 包的重要成员。本文将结合 Java 内存模型,分析 JDK 源代码,探索 ConcurrentHashMap 高并发的具体实现机制。 因为 ConcurrentHashMap 的源代码实现依赖于 Java 内存模型,因此阅读本文须要读者了解 Java 内存模型。同时,ConcurrentHashMap 的源代码会涉及到散列算法和链表数据结构,因此,读者须要对散列算法和基于链表的数据结构有所了解。java
###Java 内存模型### Java 语言的内存模型由一些规则组成,这些规则肯定线程对内存的访问如何排序以及什么时候能够确保它们对线程是可见的。下面咱们将分别介绍 Java 内存模型的重排序,内存可见性和 happens-before 关系。 ####重排序#### 内存模型描述了程序的可能行为。具体的编译器实现能够产生任意它喜欢的代码 -- 只要全部执行这些代码产生的结果,可以和内存模型预测的结果保持一致。这为编译器实现者提供了很大的自由,包括操做的重排序。 编译器生成指令的次序,能够不一样于源代码所暗示的“显然”版本。重排序后的指令,对于优化执行以及成熟的全局寄存器分配算法的使用,都是大有脾益的,它使得程序在计算性能上有了很大的提高。 重排序类型包括:算法
##ConcurrentHashMap原理实现## ###锁分离 (Lock Stripping)### 好比HashTable是一个过期的容器类,经过使用synchronized来保证线程安全,在线程竞争激烈的状况下HashTable的效率很是低下。缘由是全部访问HashTable的线程都必须竞争同一把锁。那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不一样数据段的数据时,线程间就不会存在锁竞争,从而能够有效的提升并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。 ConcurrentHashMap内部使用段(Segment)来表示这些不一样的部分,每一个段其实就是一个小的hash table,它们有本身的锁。只要多个修改操做发生在不一样的段上,它们就能够并发进行。一样当一个线程占用锁访问其中一个段数据的时候,其余段的数据也能被其余线程访问。 ConcurrentHashMap有些方法须要跨段,好比size()和containsValue(),它们可能须要锁定整个表而而不只仅是某个段,这须要按顺序锁定全部段,操做完毕后,又按顺序释放全部段的锁。这里"按顺序"是很重要的,不然极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,而且其成员变量实际上也是final的,可是,仅仅是将数组声明为final的并不保证数组成员也是final的,这须要实现上的保证。这能够确保不会出现死锁,由于得到锁的顺序是固定的。不变性是多线程编程占有很重要的地位,下面还要谈到。 final Segment<K,V>[] segments; ###不变(Immutable)和易变(Volatile)### ConcurrentHashMap彻底容许多个读操做并发进行,读操做并不须要加锁。若是使用传统的技术,如HashMap中的实现,若是容许能够在hash链的中间添加或删除元素,读操做不加锁将获得不一致的数据。ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。HashEntry表明每一个hash链中的一个节点,其结构以下所示:编程
static final class HashEntry<K,V> { final K key; // 声明 key 为 final 型 final int hash; // 声明 hash 值为 final 型 volatile V value; // 声明 value 为 volatile 型 final HashEntry<K,V> next; // 声明 next 为 final 型 HashEntry(K key, int hash, HashEntry<K,V> next, V value) { this.key = key; this.hash = hash; this.next = next; this.value = value; } }
能够看到除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,由于这须要修改next引用值,全部的节点的修改只能从头部开始。对于put操做,能够一概添加到Hash链的头部。可是对于remove操做,可能须要从中间删除一个节点,这就须要将要删除节点的前面全部节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。这在讲解删除操做时还会详述。为了确保读操做可以看到最新的值,将value设置成volatile,这避免了加锁。数组
##ConcurrentHashMap具体实现## ###ConcurrentHashMap初始化### ConcurrentHashMap初始化方法是经过initialCapacity,loadFactor, concurrencyLevel几个参数来初始化segments数组,段偏移量segmentShift,段掩码segmentMask和每一个segment里的HashEntry数组,初始化segments数组。让咱们来看一下初始化segmentShift,segmentMask和segments数组的源代码。缓存
http://java.chinaitlab.com/line/914247.html http://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap http://www.blogjava.net/DLevin/archive/2013/10/18/405030.html安全