ConcurrentHashMap 和 Hashtable 的區別主要體現在實現線程安全的方式上不同。
底層數據結構: JDK1.7的 ConcurrentHashMap 底層採用 分段的數組+鏈表 實現,JDK1.8 採用的數據結構跟
HashMap1.8的結構一樣,數組+鏈表/紅黑二叉樹。Hashtable 和 JDK1.8 之前的 HashMap 的底層數據結構類
似都是採用 數組+鏈表 的形式,數組是 HashMap 的主體,鏈表則是主要為了解決哈希衝突而存在的;
實現線程安全的方式(重要): ① 在JDK1.7的時候,ConcurrentHashMap(分段鎖) 對整個桶數組進行了
分割分段(Segment),每一把鎖只鎖容器其中一部分數據,多線程訪問容器裡不同數據段的數據,就不會存在鎖
競爭,提高併發訪問率。 到了 JDK1.8 的時候已經摒棄了Segment的概念,而是直接用 Node 數組+鏈表+紅黑
樹的數據結構來實現,併發控制使用 synchronized 和 CAS 來操作。(JDK1.6以後 對 synchronized鎖做了很
多優化) 整個看起來就像是優化過且線程安全的 HashMap,雖然在JDK1.8中還能看到 Segment 的數據結構,
但是已經簡化了屬性,只是為了兼容舊版本;② Hashtable(同一把鎖) :使用 synchronized 來保證線程安全,
效率非常低下。當一個線程訪問同步方法時,其他線程也訪問同步方法,可能會進入阻塞或輪詢狀態,如使用
put 添加元素,另一個線程不能使用 put 添加元素,也不能使用 get,競爭會越來越激烈效率越低。
兩者對比圖:
ConcurrentHashMap線程安全的具體實現方式/底層具體實現
JDK1.7(上面有示意圖)
首先將數據分為一段一段的存儲,然後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據時,其他段的
數據也能被其他線程訪問。
ConcurrentHashMap 是由 Segment 數組結構和 HashEntry 數組結構組成。
Segment 實現了 ReentrantLock,所以 Segment 是一種可重入鎖,扮演鎖的角色。HashEntry 用於存儲鍵值對數據。
static class Segmentextends ReentrantLock implements Serializable {}
一個 ConcurrentHashMap 裡包含一個 Segment 數組。Segment 的結構和HashMap類似,是一種數組和鏈表結
構,一個 Segment 包含一個 HashEntry 數組,每個 HashEntry 是一個鏈表結構的元素,每個 Segment 守護著一個
HashEntry數組裡的元素,當對 HashEntry 數組的數據進行修改時,必須首先獲得對應的 Segment的鎖。
JDK1.8 (上面有示意圖)ConcurrentHashMap取消了Segment分段鎖,採用CAS和synchronized來保證併發安全。數據結構跟HashMap1.8
的結構類似,數組+鏈表/紅黑二叉樹。
synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不衝突,就不會產生併發,效率又提升N倍。
講到最後,給粉絲們送一波福利,關注並轉發文章後,私信找我要資料,回覆“Java面試”
閱讀更多 互聯網極客 的文章