1. 程式人生 > >HashMap、Hashtable、ConcurrentHashMap對比

HashMap、Hashtable、ConcurrentHashMap對比

1.執行緒不安全的HashMap
因為多執行緒環境下,使用Hashmap進行put操作會引起死迴圈,導致CPU利用率接近100%,所以在併發情況下不能使用HashMap。
在這裡插入圖片描述

2.效率低下的HashTable容器
HashTable容器使用synchronized來保證執行緒安全,但線上程競爭激烈的情況下HashTable的效率非常低下。因為當一個執行緒訪問HashTable的同步方法時,其他執行緒訪問HashTable的同步方法時,可能會進入阻塞或輪詢狀態。如執行緒1使用put進行新增元素,執行緒2不但不能使用put方法新增元素,並且也不能使用get方法來獲取元素,所以競爭越激烈效率越低。
在這裡插入圖片描述


3.ConcurrentHashMap的鎖分段技術
HashTable容器在競爭激烈的併發環境下表現出效率低下的原因,是因為所有訪問HashTable的執行緒都必須競爭同一把鎖,那假如容器裡有多把鎖,每一把鎖用於鎖容器其中一部分資料,那麼當多執行緒訪問容器裡不同資料段的資料時,執行緒間就不會存在鎖競爭,從而可以有效的提高併發訪問效率,這就是ConcurrentHashMap所使用的鎖分段技術,首先將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問。
在這裡插入圖片描述
ConcurrentHashMap是由Segment陣列結構和MapEntry陣列結構組成
。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裡扮演鎖的角色,MapEntry則用於儲存鍵值對資料。一個ConcurrentHashMap裡包含一個Segment陣列,Segment的結構和HashMap類似,是一種陣列和連結串列結構, 一個Segment裡包含一個MapEntry陣列,每個MapEntry是一個連結串列結構的元素, 每個Segment守護者一個MapEntry數組裡的元素,當對MapEntry陣列的資料進行修改時,必須首先獲得它對應的Segment鎖。

get操作的高效之處在於整個get過程不需要加鎖,除非讀到的值是空的才會加鎖重讀,我們知道HashTable容器的get方法是需要加鎖的,那麼ConcurrentHashMap的get操作是如何做到不加鎖的呢?原因是它的get方法裡將要使用的共享變數都定義成volatile,如用於統計當前Segement大小的count欄位和用於儲存值的HashEntry的value。定義成volatile的變數,能夠線上程之間保持可見性,能夠被多執行緒同時讀,並且保證不會讀到過期的值,但是隻能被單執行緒寫(有一種情況可以被多執行緒寫,就是寫入的值不依賴於原值),在get操作裡只需要讀不需要寫共享變數count和value,所以可以不用加鎖。之所以不會讀到過期的值,是根據java記憶體模型的happen before原則,對volatile欄位的寫入操作先於讀操作,即使兩個執行緒同時修改和獲取volatile變數,get操作也能拿到最新的值,這是用volatile替換鎖的經典應用場景。

那麼ConcurrentHashMap是如何判斷在統計的時候容器是否發生了變化呢?使用modCount變數,在put , remove和clean方法裡操作元素前都會將變數modCount進行加1,那麼在統計size前後比較modCount是否發生變化,從而得知容器的大小是否發生變化。

在ConcurrentHashMap沒有出現以前,jdk使用hashtable來實現執行緒安全,但是hashtable是將整個hash表鎖住,所以效率很低下。

ConcurrentHashMap將資料分別放到多個Segment中,預設16個,每一個Segment中又包含了多個HashEntry列表陣列,

對於一個key,需要經過三次hash操作,才能最終定位這個元素的位置,這三次hash分別為:

  1. 對於一個key,先進行一次hash操作,得到hash值h1,也即h1 = hash1(key);
  2. 將得到的h1的高几位進行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通過h2能夠確定該元素的放在哪個Segment;
  3. 將得到的h1進行第三次hash,得到hash值h3,也即h3 = hash3(h1),通過h3能夠確定該元素放置在哪個HashEntry。

每一個Segment都擁有一個鎖,當進行寫操作時,只需要鎖定一個Segment,而其它Segment中的資料是可以訪問的。