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執行緒
- 多執行緒高併發程式設計(10) -- ConcurrentHashMap原始碼分析執行緒程式設計HashMap原始碼
- ConcurrentHashMap一定執行緒安全?HashMap執行緒
- 執行緒池原始碼分析執行緒原始碼
- 分析.Net裡執行緒同步機制執行緒
- OpenMP 執行緒同步 Construct 實現原理以及原始碼分析(上)執行緒Struct原始碼
- OpenMP 執行緒同步 Construct 實現原理以及原始碼分析(下)執行緒Struct原始碼
- Android/java 多執行緒(一)-Thread的使用以及原始碼分析AndroidJava執行緒thread原始碼
- ConcurrentHashMap 原始碼分析HashMap原始碼
- ConcurrentHashMap的size方法是執行緒安全的嗎?HashMap執行緒
- Python執行緒池ThreadPoolExecutor原始碼分析Python執行緒thread原始碼
- Dubbo RPC執行緒模型 原始碼分析RPC執行緒模型原始碼
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒同步機制執行緒
- Netty原始碼死磕一(netty執行緒模型及EventLoop機制)Netty原始碼執行緒模型OOP
- 一起分析執行緒的狀態及執行緒通訊機制執行緒
- Java多執行緒之Thread原始碼分析Java執行緒thread原始碼
- langchain chatchat執行機制原始碼解析LangChain原始碼
- 多執行緒十二之ConcurrentHashMap1.8實現分析執行緒HashMap
- JavaScript執行緒機制與事件機制JavaScript執行緒事件
- Java排程執行緒池ScheduledThreadPoolExecutor原始碼分析Java執行緒thread原始碼
- jdk1.8 執行緒池部分原始碼分析JDK執行緒原始碼
- 多執行緒基礎(十九):Semaphore原始碼分析執行緒原始碼
- React-原始碼解析-setState執行機制React原始碼
- 比特幣原始碼分析:多執行緒檢查指令碼比特幣原始碼執行緒指令碼
- 詳解Java執行緒池的ctl(執行緒池控制狀態)【原始碼分析】Java執行緒原始碼
- HashMap為何執行緒不安全?HashMap,HashTable,ConcurrentHashMap對比HashMap執行緒
- java synchronize - 執行緒同步機制Java執行緒
- 執行緒鎖 -賣票機制執行緒
- 使用 Executors,ThreadPoolExecutor,建立執行緒池,原始碼分析理解thread執行緒原始碼
- JUC(4)---java執行緒池原理及原始碼分析Java執行緒原始碼
- Netty原始碼分析之Reactor執行緒模型詳解Netty原始碼React執行緒模型
- Java多執行緒類FutureTask原始碼閱讀以及淺析Java執行緒原始碼
- 執行緒池原始碼探究執行緒原始碼