多執行緒環境下的ConcurrentHashMap
文章目錄
什麼是ConcurrentHashMap?
ConcurrentHashMap並像HashMap一樣在java.util包下,而是在java.util.concurrent包下,可見ConcurrentHashMap是為併發而存在的。ConcurrentHashMap是高併發環境下使用的HashMap,通過volatile、CAS、synchronized等來實現了執行緒安全的HashMap。
底層資料結構?
由於取消了segment,Java1.8中的ConcurrentHashMap跟HashMap的資料結構類似,參見上一篇HashMap。
Java1.8中引入了TreeBin來維護對桶內TreeNode的引用以及鎖狀態。
如何實現併發安全?
Java1.8中主要是通過CAS和synchronized來實現多執行緒安全的。
對於CAS有三個Table element access方法:
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node< K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject (tab, ((long)i << ASHIFT) + ABASE, c, v);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
put操作和HashMap一樣還是使用putVal函式實現,不同的是,在當前位置無元素時,使用CAS來新增Node結點,如果失敗,則繼續迴圈,迴圈條件是
for (Node<K,V>[] tab = table;;)
直到CAS成功才break。
如果當前位置有元素,則對當前結點即頭結點使用synchronized,縮小了鎖的粒度。
get操作並沒有加鎖,並不能完全保證多執行緒的一致性。
size操作返回的也不是一個精確值。
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
transfer和ForwardingNode
transfer(Node<K,V>[] tab, Node<K,V>[] nextTab)是在擴容時使用的函式,它會將舊桶上的結點拷貝到新桶(Moves and/or copies the nodes in each bin to new table)。在結點中有一類Special Nodes。其中在transfer操作時插入到桶的頭結點的特殊結點叫做ForwardingNode(A node inserted at head of bins during transfer operations)。可以把該結點稱為轉發結點,因為在擴容時,對舊table的訪問,會有該結點轉發到新table上。建構函式為:
ForwardingNode(Node<K,V>[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
在transfer操作時,會呼叫此操作,
Hash計算
put時的hash計算藉助了spread函式
int hash = spread(key.hashCode());
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;//HASH_BITS = 0x7fffffff;
}