ConcurrentHashMap執行緒安全機制以及原始碼分析
1. JDK8中基於HashEntry(首節點)進行執行緒安全同步保證(在首節點進行加鎖)
2. 通過Node+CAS+Synchronized機制來完成
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
put操作
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
//如果長度為0,需要對陣列進行初始化
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//如果沒有發生hash衝突,則會被封裝成一個Node節點,放置到陣列下標所對應的位置上面
//通過CAS進行放置,因為在多執行緒的情況下,可能同時有多個執行緒想對同一個位置進行放入節點的操作
//保證只有一個執行緒把節點放入到正確的位置上
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//如果concurrentHashMap正在移動,比如正在擴容,需要呼叫helpTransfer將節點放入至正確的位置:流程是類似的
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
//表示目標位置已經有元素了,也即發生了hash衝突。通過拉鍊法解決衝突
else {
V oldVal = null;
//利用了synchronized對頭節點進行了鎖定,保證只有一個執行緒能進行拉鍊法
synchronized (f) {
//接下來的操作和hashMap中的是類似的
if (tabAt(tab, i) == f) {
if (fh >= 0) {
get操作
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
沒有使用任何的同步或者cas機制來保證執行緒安全
為什麼get不需要加鎖?
- 由於Node的元素val和指標next是用volatile修飾的,執行緒A修改一個結點或者新增一個節點的時候,對執行緒B可見的
- 陣列同時也使用volatile修飾,保證在陣列擴容的時候保證可見性
- 對key和value包裹的Node中hash、key都是用final進行了修飾,意味著建立了以後就不能再進行修改了
總結
- 在JDK8中ConcurrentHashMap的get操作全程不需要加鎖,put操作通過CAS+Synchronized關鍵字來保證執行緒安全
- 這樣的設計機制保證了,它比其他同步容器如HashTable、Collections.synchronizedMap()進行包裝效率高的原因
相關文章
- 從原始碼分析ConcurrentHashMap執行緒安全和高效的特性原始碼HashMap執行緒
- ConcurrentHashMap執行緒安全嗎?HashMap執行緒
- ConcurrentHashMap原始碼解析,多執行緒擴容HashMap原始碼執行緒
- 【Java】ConcurrentHashMap執行緒安全技巧JavaHashMap執行緒
- Android執行緒間訊息機制-Handler原始碼分析(FrameWork)Android執行緒原始碼Framework
- 多執行緒高併發程式設計(10) -- ConcurrentHashMap原始碼分析執行緒程式設計HashMap原始碼
- ConcurrentHashMap一定執行緒安全?HashMap執行緒
- 執行緒池原始碼分析執行緒原始碼
- 分析.Net裡執行緒同步機制執行緒
- dubbo原始碼-執行緒池分析原始碼執行緒
- 多執行緒-執行緒安全問題的產生原因分析以及同步程式碼塊的方式解決執行緒安全問題執行緒
- OpenMP 執行緒同步 Construct 實現原理以及原始碼分析(上)執行緒Struct原始碼
- OpenMP 執行緒同步 Construct 實現原理以及原始碼分析(下)執行緒Struct原始碼
- netty原始碼分析-執行緒池Netty原始碼執行緒
- quartz執行緒管理的原始碼分析quartz執行緒原始碼
- 執行緒同步機制執行緒
- ConcurrentHashMap 原始碼分析HashMap原始碼
- ConcurrentHashMap的size方法是執行緒安全的嗎?HashMap執行緒
- strerror執行緒安全分析Error執行緒
- 深入分析Java執行緒中斷機制Java執行緒
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- langchain chatchat執行機制原始碼解析LangChain原始碼
- 一起分析執行緒的狀態及執行緒通訊機制執行緒
- JavaScript執行緒機制與事件機制JavaScript執行緒事件
- Dubbo RPC執行緒模型 原始碼分析RPC執行緒模型原始碼
- 從原始碼分析非執行緒安全集合類的不安全迭代器原始碼執行緒
- Android/java 多執行緒(一)-Thread的使用以及原始碼分析AndroidJava執行緒thread原始碼
- 多執行緒十二之ConcurrentHashMap1.8實現分析執行緒HashMap
- React-原始碼解析-setState執行機制React原始碼
- Python執行緒池ThreadPoolExecutor原始碼分析Python執行緒thread原始碼
- jdk1.8 執行緒池部分原始碼分析JDK執行緒原始碼
- Java多執行緒之Thread原始碼分析Java執行緒thread原始碼
- 多執行緒基礎(十九):Semaphore原始碼分析執行緒原始碼
- Netty原始碼死磕一(netty執行緒模型及EventLoop機制)Netty原始碼執行緒模型OOP
- 執行緒鎖 -賣票機制執行緒
- java synchronize - 執行緒同步機制Java執行緒
- quartz的執行緒池機制quartz執行緒