Java 併發---ConcurrentHashMap

concurrent包下的併發容器

JDK5中添加了新的concurrent包,相對同步容器而言,併發容器經過一些機制改進了併發性能。由於同步容器將全部對容器狀態的訪問都串行化了,這樣保證了線程的安全性,因此這種方法的代價就是嚴重下降了併發性,當多個線程競爭容器時,吞吐量嚴重下降。所以Java5開始針對多線程併發訪問設計,提供了併發性能較好的併發容器,引入了java.util.concurrent包。java

與Vector、HashTable、Collections.synchronizedXxx()等同步容器相比,util.concurrent中引入的併發容器主要解決了兩個問題:編程

  1. 根據具體場景進行設計,儘可能避免synchronized,提供併發性。
  2. 定義了一些併發安全的複合操做,而且保證併發環境下的迭代操做不會出錯。

util.concurrent中容器在迭代時,能夠不封裝在synchronized中,能夠保證不拋異常,可是未必每次看到的都是"最新的、當前的"數據。數組

下面是對併發容器的簡單介紹:安全

ConcurrentHashMap代替同步的Map(Collections.synchronized(new HashMap())),衆所周知,HashMap是根據散列值分段存儲的,同步Map在同步的時候鎖住了全部的段,而ConcurrentHashMap加鎖的時候根據散列值鎖住了散列值鎖對應的那段,所以提升了併發性能。ConcurrentHashMap也增長了對經常使用複合操做的支持,好比"若沒有則添加":putIfAbsent(),替換:replace()。這2個操做都是原子操做。數據結構

CopyOnWriteArrayList和CopyOnWriteArraySet分別代替List和Set,主要是在遍歷操做爲主的狀況下來代替同步的List和同步的Set,這也就是上面所述的思路:迭代過程要保證不出錯,除了加鎖,另一種方法就是"克隆"容器對象。多線程

ConcurrentLinkedQueue是一個先進先出的隊列。它是非阻塞隊列。併發

ConcurrentSkipListMap能夠在高效併發中替代SortedMap(例如用Collections.synchronizedMap包裝的TreeMap)。性能

ConcurrentSkipListSet能夠在高效併發中替代SortedSet(例如用Collections.synchronizedSet包裝的TreeMap)。線程

爲何使用ConcurrentHashMap

線程不安全的HashMap

併發編程中使用HashMap可能致使程序死循環,致使cpu利用率接近100%。具體緣由是,再執行put操做時會引發死循環,多線程會致使HashMap的Entry鏈表造成環形數據結構,這樣的話Entry的next節點永遠不爲空,就會產生死循環獲取Entry。設計

效率低下的HashTable

HashTable使用synchronized來保證線程安全,競爭激烈狀況下,當一個線程訪問同步方法,其餘線程也訪問同步方法,會進入阻塞或輪詢狀態。線程1使用put進行元素添加,線程2不但不能使用put方法,也不能使用get方法。

ConcurrentHashMap使用鎖分段技術

ConcurrentHashMap將數據分紅一段一段地存儲,而後給每一段數據配一把鎖,而且其內部的結構可讓其在進行寫操做的時候可以將鎖的粒度保持儘可能的小,不用對整個ConcurrentHashMap加鎖。當一個線程佔用鎖訪問其中一個段數據的時候,其餘段地數據也能被其餘線程訪問。

ConcurrentHashMap的結構

ConcurrentHashMap是由Segment數組結構和HashEntry數組結構組成。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裏扮演鎖的角色,HashEntry則用於存儲鍵值對數據。一個ConcurrentHashMap裏包含一個Segment數組,Segment的結構和HashMap相似,是一種數組和鏈表結構, 一個Segment裏包含一個HashEntry數組,每一個HashEntry是一個鏈表結構的元素, 每一個Segment守護着一個HashEntry數組裏的元素,當對HashEntry數組的數據進行修改時,必須首先得到它對應的Segment鎖。
ConcurrentHashMap結構圖

ConcurrentHashMap和HashTable的區別圖
ConcurrentHashMap和HashTable的區別圖

從上面的結構咱們能夠了解到,ConcurrentHashMap定位一個元素的過程須要進行兩次Hash操做,第一次Hash定位到Segment,第二次Hash定位到元素所在的鏈表的頭部,所以,這一種結構的帶來的反作用是Hash的過程要比普通的HashMap要長,可是帶來的好處是寫操做的時候能夠只對元素所在的Segment進行加鎖便可,不會影響到其餘的Segment,這樣,在最理想的狀況下,ConcurrentHashMap能夠最高同時支持Segment數量大小的寫操做(恰好這些寫操做都很是平均地分佈在全部的Segment上),因此,經過這一種結構,ConcurrentHashMap的併發能力能夠大大的提升。

相關文章
相關標籤/搜索