##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安全