ConcurrentHashMap原始碼分析

2022-09-12 05:27:17 字數 3004 閱讀 2759

出處:

concurrenthashmap 是 j**a 並發包中提供的乙個執行緒安全且高效的 hashmap 實現,concurrenthashmap 在併發程式設計的場景中使用頻率非常之高,本文就來分析下 concurrenthashmap 的實現原理,並對其實現原理進行分析(jdk1.7)。

hashmap:先說 hashmap,hashmap 是執行緒不安全,在併發環境下,可能會形成環形鍊錶(擴容時可能造成),導致 get 操作時,cpu 空轉,所以,在併發環境中使用 hashmap 是非常危險的。

hashtable:hashtable 和 hashmap 的實現原理幾乎是一樣,差別無非就是1. hashtable 不允許 key 和 value 為 null;2. hashtable 是執行緒安全的。但是 hashtable 執行緒安全的策略實現代價太大了,簡單粗暴,get / put 所有相關操作都是 synchronized 的,這相當於給整個雜湊表加了一把大鎖,多執行緒訪問的時候,只要有乙個執行緒訪問或操作該物件,那其他執行緒只能阻塞,相當於將所有的操作序列化,在競爭激烈的併發場景中效能就會非常差。

hashtable 效能差主要是由於所有操作需要競爭同一把鎖,而如果容器中有多把鎖,每一把鎖鎖一段資料,這樣在多執行緒訪問時不同段的資料時,就不會存在鎖競爭了,這樣便可以有效地提高併發效率。這就是 concurrenthashmap 所採用的分段鎖思想。

concurrenthashmap 採用了非常精妙的「分段鎖」策略,concurrenthashmap 的主幹是個 segment 陣列。

final segment segments;
segment 繼承了 reentrantlock,所以它就是一種可重入鎖(reentrantlock)。在 concurrenthashmap,乙個 segment 就是乙個子雜湊表,segment 裡維護了乙個 hashentry 陣列,併發環境下,對於不同 segment 的資料進行操作是不用考慮鎖競爭的。(就按預設的 concurrentleve 為 16 來講,理論上就允許 16 個執行緒併發執行,有木有很酷)

所以,對於同乙個 segment 的操作才需要考慮執行緒同步,不同的 segment 則無需考慮。

segment 類似於 hashmap,乙個 segment 維護著乙個 hashentry 陣列。

transient volatile hashentry table;
hashentry 是目前我們提到的最小的邏輯處理單元了。乙個 concurrenthashmap 維護乙個 segment 陣列,乙個 segment 維護乙個 hashentry 陣列。

static final class hashentry
我們說 segment 類似雜湊表,那麼一些屬性就跟我們之前提到的 hashmap 差不了多少,比如負載因子 loadfactor,比如閾值 threshold 等等,看下 segment 的構造方法:

segment(float lf,int threshold,hashentrytab)
我們來看下 concurrenthashmap 的構造方法

public concurrenthashmap(int initialcapacity,float loadfactor,int concurrencylevel)
初始化方法有三個引數,如果使用者不指定則會使用預設值,initialcapacity 為 16,loadfactor 為 0.75(負載因子,擴容時需要參考),concurrentlevel 為 16。

從上面的**可以看出來,segment 陣列的大小 ssize 是由 concurrentlevel 來決定的,但是卻不一定是 concurrentlevel,ssize 一定是大於或等於 concurrentlevel 的最小 2 的次冪。比如:預設情況下 concurrentlevel 是 16,則 ssize 為 16;弱 concurrentlevel 為 14,ssize 為 16;若 concurrentlevel 為 17,則 ssize 為 32。為什麼 segment 的陣列大小一定是 2 的次冪?其實主要是便於通過按位與的雜湊演算法來定位 segment 的 index。至於更詳細的原因,有興趣的話可以參考另一篇 文章 ,其中對於陣列長度為什麼一定要是 2 的次冪有較為詳細的分析。

接下來,我們看一下 put 方法

public v put(k key,v value)}}

return null;

}

get 方法無需加鎖,由於其中涉及到的共享變數都是用 volatile 修飾,volatile 可以保證記憶體可見性。只不過是鎖粒度細了而已

put 方法

concurrenthashmap put 方法是呼叫了 segment 的 put 方法,segment 的 put 方法是要加鎖的,只不過是鎖粒度細了而已。

final v put(k key, int hash, v value, boolean onlyifabsent) 

break;

}e = e.next;

}else

}} finally

return oldvalue;

}

concurrenthashmap 作為一種執行緒安全且高效的雜湊表解決方案,尤其其中的 "分段鎖" 的方案,相比 hashtable 的全表鎖在效能上的提公升非常之大。

ConcurrentHashMap原始碼分析

hashmap 先說hashmap,hashmap是執行緒不安全 的,在併發環境下,可能會形成環狀鍊錶 hashtable hashtable和hashmap的實現原理幾乎一樣,差別無非是1.hashtable不允許key和value為null 2.hashtable是執行緒安全的。但是hashta...

ConcurrentHashMap原始碼詳解

成員變數private static final int maximum capacity 1 30 private static final int default capacity 16 static final int max array size integer.max value 8 pr...

concurrentHashMap原始碼分析

concurrenthashmap是hashmap的執行緒安全版本,內部也是使用 陣列 鍊錶 紅黑樹 的結構來儲存元素。相比於同樣執行緒安全的hashtable來說,效率等各方面都有極大地提高。在這裡可以使用上篇那個 進行測試,根據結果可以知道concurrenthashmap是執行緒安全的,由於分...