爲何把這兩個數據結構對比分析呢,相信你們都明白。首先兩者都是線程安全的,可是兩者保證線程安全的方式倒是不一樣的。廢話很少說了,從源碼的角度分析一下二者的異同,首先給出兩者的繼承關係圖。java
咱們仍是先給出一張Hashtable類的屬性和方法圖,其中Entry<K,V>是Hashtable類的靜態內部類,該類繼承自Map.Entry<K,V>接口。以下將會詳細講解Hashtable類中屬性和方法的含義。node
public static void main(String[] args) { Hashtable<Integer, String> tb = new Hashtable<Integer,String>(); tb.put(1, "BUPT"); tb.put(2, "PKU"); tb.put(3, "THU"); Iterator<Entry<Integer, String>> iter = tb.entrySet().iterator(); while(iter.hasNext()){ Entry<?, ?> entry = (Entry<?, ?>) iter.next(); //此處會拋出異常 System.out.println(entry.getValue()); if("THU".equals(entry.getValue())){ tb.remove(entry.getKey()); } } } /* 輸出結果以下: THU Exception in thread "main" java.util.ConcurrentModificationException at java.util.Hashtable$Enumerator.next(Hashtable.java:1367) at ali.Main.main(Main.java:16) */
Hashtable的remove(Object key)方法見以下方法5,每一次修改hashtable中的數據都更新modCount的值。Hashtable內部類Enumerator<T>的相關部分代碼以下:算法
private class Enumerator<T> implements Enumeration<T>, Iterator<T> { Entry<?,?>[] table = Hashtable.this.table; int index = table.length; Entry<?,?> entry; Entry<?,?> lastReturned; int type; /** * Indicates whether this Enumerator is serving as an Iterator * or an Enumeration. (true -> Iterator). */ boolean iterator; /** * 遍歷之初將hashtable修改的次數賦值給expectedModCount */ protected int expectedModCount = modCount; Enumerator(int type, boolean iterator) { this.type = type; this.iterator = iterator; } // public boolean hasMoreElements() { Entry<?,?> e = entry; int i = index; Entry<?,?>[] t = table; /* Use locals for faster loop iteration */ while (e == null && i > 0) { e = t[--i]; } entry = e; index = i; return e != null; } @SuppressWarnings("unchecked") public T nextElement() { Entry<?,?> et = entry; int i = index; Entry<?,?>[] t = table; /* Use locals for faster loop iteration */ while (et == null && i > 0) { et = t[--i]; } entry = et; index = i; if (et != null) { Entry<?,?> e = lastReturned = entry; entry = e.next; return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e); } throw new NoSuchElementException("Hashtable Enumerator"); } //查看是否還有下一個元素 public boolean hasNext() { return hasMoreElements(); } public T next() { //首先判斷modCount和expectedModCount是否相等 //因爲在主程序中Hashtable對象經過tb.remove()方法修改了modCount的值,使得expectedModCount和modCount不相等而拋出異常 //解決辦法就是將tb.remove()方法替換爲iter.remove()方法 if (modCount != expectedModCount) throw new ConcurrentModificationException(); return nextElement(); } //該方法在remove元素的同時修改了modCount和expectedModCount的值 public void remove() { if (!iterator) throw new UnsupportedOperationException(); if (lastReturned == null) throw new IllegalStateException("Hashtable Enumerator"); if (modCount != expectedModCount) throw new ConcurrentModificationException(); synchronized(Hashtable.this) { Entry<?,?>[] tab = Hashtable.this.table; int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) { if (e == lastReturned) { modCount++; expectedModCount++; if (prev == null) tab[index] = e.next; else prev.next = e.next; count--; lastReturned = null; return; } } throw new ConcurrentModificationException(); } } }
get(Object key),獲取當前鍵key所對應的value值,本方法和containsKey(Object key)方法除了返回值其它都相同,若是能找到該key對應的value,則返回value的值,若是不能則返回null。數組
put(K key, V value),將該鍵值對加入table中。首先插入的value不能爲空。其次若是當前插入的key值已經在table中存在,則用新的value替換掉原來的value值,並將原來的value值做爲該方法的返回值返回。若是當前插入的key不在table中,則將該鍵值對插入。安全
插入的方法首先判斷當前table中的值是否大於閾值(threshold),若是大於該閾值,首先對該表擴容,再將新的鍵值對插入table[index]的鏈表的第一個Entry的位置上。數據結構
replace(K key, V value),將鍵爲key的Entry對象值更新爲value,並將原來的value最爲該方法的返回值。多線程
ConcurrentHashMap在JDK1.8中改動仍是挺大的。它摒棄了Segment(段鎖)的概念,在實現上採用了CAS算法。底層使用數組+鏈表+紅黑樹的方式,可是爲了作到併發,同時也增長了大量的輔助類。以下是ConcurrentHashMap的類圖。併發
//ConcurrentHashMap最大容量 private static final int MAXIMUM_CAPACITY = 1 << 30; //ConcurrentHashMap初始默認容量 private static final int DEFAULT_CAPACITY = 16; //最大table數組的大小 static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //默認並行級別,主體代碼並未使用 private static final int DEFAULT_CONCURRENCY_LEVEL = 16; //加載因子,默認爲0.75 private static final float LOAD_FACTOR = 0.75f; //當hash桶中hash衝突的數目大於此值時,將鏈表轉化爲紅黑樹,加快hash的查找速度 static final int TREEIFY_THRESHOLD = 8; //當hash桶中hash衝突小於等於此值時,會把紅黑樹轉化爲鏈表 static final int UNTREEIFY_THRESHOLD = 6; //當table數組的長度大於該值時,同時知足hash桶中hash衝突大於TREEIFY_THRESHOLD時,纔會把鏈表轉化爲紅黑樹 static final int MIN_TREEIFY_CAPACITY = 64; //擴容操做中,transfer()方法容許多線程,該值表示一個線程執行transfer時,至少對連續的多少個hash桶進行transfer private static final int MIN_TRANSFER_STRIDE = 16; //ForwardingNode的hash值,ForwardingNode是一種臨時節點,在擴容中才會出現,不存儲實際的數據 static final int MOVED = -1; //TreeBin的hash值,TreeBin是用於代理TreeNode的特殊節點,存儲紅黑樹的根節點 static final int TREEBIN = -2; //用於和負數hash進行&運算,將其轉化爲正數 static final int HASH_BITS = 0x7fffffff;
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; } public final K getKey() { return key; } public final V getValue() { return val; } public final int hashCode() { return key.hashCode() ^ val.hashCode(); } public final String toString(){ return key + "=" + val; } //不支持直接設置value的值 public final V setValue(V value) { throw new UnsupportedOperationException(); } public final boolean equals(Object o) { Object k, v, u; Map.Entry<?,?> e; return ((o instanceof Map.Entry) && (k = (e = (Map.Entry<?,?>)o).getKey()) != null && (v = e.getValue()) != null && (k == key || k.equals(key)) && (v == (u = val) || v.equals(u))); } //從當前節點查找對應的鍵爲k的Node<K,V> Node<K,V> find(int h, Object k) { Node<K,V> e = this; if (k != null) { do { K ek; if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; } while ((e = e.next) != null); } return null; } }
static final class TreeNode<K,V> extends Node<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; //當前節點的前一個結點,從而方便刪除 TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next, TreeNode<K,V> parent) { super(hash, key, val, next); this.parent = parent; } Node<K,V> find(int h, Object k) { return findTreeNode(h, k, null); } //查找hashcode爲h,key爲k的TreeNode結點 final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) { if (k != null) { TreeNode<K,V> p = this; do { int ph, dir; K pk; TreeNode<K,V> q; TreeNode<K,V> pl = p.left, pr = p.right; if ((ph = p.hash) > h) p = pl; else if (ph < h) p = pr; else if ((pk = p.key) == k || (pk != null && k.equals(pk))) return p; else if (pl == null) p = pr; else if (pr == null) p = pl; else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0) p = (dir < 0) ? pl : pr; else if ((q = pr.findTreeNode(h, k, kc)) != null) return q; else p = pl; } while (p != null); } return null; } }
static final class ForwardingNode<K,V> extends Node<K,V> { final Node<K,V>[] nextTable; //構造函數指定hash值爲MOVED,key=null, value=null, next=null ForwardingNode(Node<K,V>[] tab) { super(MOVED, null, null, null); this.nextTable = tab; } Node<K,V> find(int h, Object k) { //for循環避免屢次碰見ForwardingNode致使遞歸過深 outer: for (Node<K,V>[] tab = nextTable;;) { Node<K,V> e; int n; if (k == null || tab == null || (n = tab.length) == 0 || (e = tabAt(tab, (n - 1) & h)) == null) return null; for (;;) { int eh; K ek; if ((eh = e.hash) == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; if (eh < 0) { //若是碰見ForwardingNode結點,則遍歷ForwardingNode的nextTable結點 if (e instanceof ForwardingNode) { tab = ((ForwardingNode<K,V>)e).nextTable; continue outer; } else return e.find(h, k); } if ((e = e.next) == null) return null; } } } }
補充圖一張說明擴容下是如何遍歷結點的。dom
static final class TreeBin<K,V> extends Node<K,V> { TreeNode<K,V> root; //紅黑樹的根節點 volatile TreeNode<K,V> first; //鏈表的頭結點 volatile Thread waiter; //最近一個設置waiter標誌位的線程 volatile int lockState; //全局的鎖狀態 // values for lockState static final int WRITER = 1; // set while holding write lock 寫鎖狀態 static final int WAITER = 2; // set when waiting for write lock 等待獲取寫鎖的狀態 static final int READER = 4; // increment value for setting read lock 讀鎖狀態,讀鎖能夠疊加,即紅黑樹能夠併發讀,每增長一個讀線程lockState的值加READER /** * 紅黑樹的讀鎖狀態和寫鎖狀態是互斥的,可是讀寫操做實際上能夠是不互斥的 * 紅黑樹的讀寫狀態互斥是指以紅黑樹的方式進行讀寫操做時互斥的 * 當線程持有紅黑樹的寫鎖時,讀線程不能以紅黑樹的方式進行讀取操做,但能夠用簡單鏈表的方式讀取,從而實現了讀寫操做的併發執行 * 當有線程持有紅黑樹的讀鎖時,寫線程會阻塞,可是紅黑樹查找速度快,所以寫線程阻塞時間短。 * put/remove/replace方法會鎖住TreeBin節點,所以不會出現寫-寫競爭。 */ //當hashCode相等且不是Comparable類時使用此方法判斷大小 static int tieBreakOrder(Object a, Object b) { int d; if (a == null || b == null || (d = a.getClass().getName(). compareTo(b.getClass().getName())) == 0) d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1); return d; } //以b爲頭節點的鏈表建立紅黑樹 TreeBin(TreeNode<K,V> b) { super(TREEBIN, null, null, null); this.first = b; TreeNode<K,V> r = null; for (TreeNode<K,V> x = b, next; x != null; x = next) { next = (TreeNode<K,V>)x.next; x.left = x.right = null; if (r == null) { x.parent = null; x.red = false; r = x; } else { K k = x.key; int h = x.hash; Class<?> kc = null; for (TreeNode<K,V> p = r;;) { int dir, ph; K pk = p.key; if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) dir = tieBreakOrder(k, pk); TreeNode<K,V> xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { x.parent = xp; if (dir <= 0) xp.left = x; else xp.right = x; r = balanceInsertion(r, x); break; } } } } this.root = r; assert checkInvariants(root); } /** * 紅黑樹重構時西藥對根節點加寫鎖 */ private final void lockRoot() { //嘗試獲取一次鎖 if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER)) contendedLock(); //直到獲取到寫鎖,該方法才返回 } /** * 釋放寫鎖 */ private final void unlockRoot() { lockState = 0; } /** * 阻塞寫線程,當寫線程獲取寫鎖時返回 *由於ConcurrentHashMap的put/remove/replace方法會對TreeBin加鎖,所以不會出現寫-寫競爭 *所以該方法只用考慮讀鎖線程阻礙線程獲取寫鎖,而不用考慮寫鎖線程阻礙線程獲取寫鎖,不用考慮寫-寫競爭 */ private final void contendedLock() { boolean waiting = false; for (int s;;) { //~WAITER表示反轉WAITER,當沒喲線程持有讀鎖時,該條件爲true if (((s = lockState) & ~WAITER) == 0) { if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) { //沒有任何線程持有讀寫鎖時,嘗試讓當前線程獲取寫鎖,同時清空waiter標識位 if (waiting) waiter = null; return; } } else if ((s & WAITER) == 0) { //當前線程持有讀鎖,而且當前線程不是WAITER狀態時,該條件爲true if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) { //嘗試佔據WAITER標識位 waiting = true; //代表本身處於waiter狀態 waiter = Thread.currentThread(); } } else if (waiting) //當前線程持有讀鎖,而且當前線程處於waiter狀態時,該條件爲true LockSupport.park(this); //阻塞本身 } } /** * 從根節點開始查找,找不到返回null * 當有寫線程加上寫鎖時,使用鏈表方式進行查找 */ final Node<K,V> find(int h, Object k) { if (k != null) { for (Node<K,V> e = first; e != null; ) { int s; K ek; //兩種特殊狀況下以鏈表的方式進行查找 //一、有線程正持有 寫鎖,這樣作可以不阻塞讀線程 //二、WAITER時,再也不繼續加 讀鎖,可以讓已經被阻塞的寫線程儘快恢復運行,或者恰好讓某個寫線程不被阻塞 if (((s = lockState) & (WAITER|WRITER)) != 0) { if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; e = e.next; } // 讀線程數量加1,讀狀態進行累加 else if (U.compareAndSwapInt(this, LOCKSTATE, s, s + READER)) { TreeNode<K,V> r, p; try { p = ((r = root) == null ? null : r.findTreeNode(h, k, null)); } finally { Thread w; // 若是這是最後一個讀線程,而且有寫線程由於 讀鎖 而阻塞,那麼要通知它,告訴它能夠嘗試獲取寫鎖了 if (U.getAndAddInt(this, LOCKSTATE, -READER) == (READER|WAITER) && (w = waiter) != null) LockSupport.unpark(w); // 讓被阻塞的寫線程運行起來,從新去嘗試獲取寫鎖 } return p; } } } return null; } /** *在ConcurrentHashMap的putVal方法若是hash桶爲紅黑樹時調用 */ final TreeNode<K,V> putTreeVal(int h, K k, V v) { Class<?> kc = null; boolean searched = false; for (TreeNode<K,V> p = root;;) { int dir, ph; K pk; if (p == null) { first = root = new TreeNode<K,V>(h, k, v, null, null); break; } else if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((pk = p.key) == k || (pk != null && k.equals(pk))) return p; else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) { if (!searched) { TreeNode<K,V> q, ch; searched = true; if (((ch = p.left) != null && (q = ch.findTreeNode(h, k, kc)) != null) || ((ch = p.right) != null && (q = ch.findTreeNode(h, k, kc)) != null)) return q; } dir = tieBreakOrder(k, pk); } TreeNode<K,V> xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { TreeNode<K,V> x, f = first; first = x = new TreeNode<K,V>(h, k, v, f, xp); if (f != null) f.prev = x; if (dir <= 0) xp.left = x; else xp.right = x; if (!xp.red) x.red = true; else { lockRoot(); try { root = balanceInsertion(root, x); } finally { unlockRoot(); } } break; } } assert checkInvariants(root); return null; } /** * 從鏈表和紅黑樹上都刪除結點 * 兩點區別:一、返回值,紅黑樹的規模過小時,返回true,調用者再去進行樹->鏈表的轉化; * 二、紅黑樹規模足夠,不用變換成鏈表時,進行紅黑樹上的刪除要加 寫鎖 */ final boolean removeTreeNode(TreeNode<K,V> p) { TreeNode<K,V> next = (TreeNode<K,V>)p.next; TreeNode<K,V> pred = p.prev; // unlink traversal pointers TreeNode<K,V> r, rl; if (pred == null) first = next; else pred.next = next; if (next != null) next.prev = pred; if (first == null) { root = null; return true; } if ((r = root) == null || r.right == null || // too small (rl = r.left) == null || rl.left == null) return true; lockRoot(); try { TreeNode<K,V> replacement; TreeNode<K,V> pl = p.left; TreeNode<K,V> pr = p.right; if (pl != null && pr != null) { TreeNode<K,V> s = pr, sl; while ((sl = s.left) != null) // find successor s = sl; boolean c = s.red; s.red = p.red; p.red = c; // swap colors TreeNode<K,V> sr = s.right; TreeNode<K,V> pp = p.parent; if (s == pr) { // p was s's direct parent p.parent = s; s.right = p; } else { TreeNode<K,V> sp = s.parent; if ((p.parent = sp) != null) { if (s == sp.left) sp.left = p; else sp.right = p; } if ((s.right = pr) != null) pr.parent = s; } p.left = null; if ((p.right = sr) != null) sr.parent = p; if ((s.left = pl) != null) pl.parent = s; if ((s.parent = pp) == null) r = s; else if (p == pp.left) pp.left = s; else pp.right = s; if (sr != null) replacement = sr; else replacement = p; } else if (pl != null) replacement = pl; else if (pr != null) replacement = pr; else replacement = p; if (replacement != p) { TreeNode<K,V> pp = replacement.parent = p.parent; if (pp == null) r = replacement; else if (p == pp.left) pp.left = replacement; else pp.right = replacement; p.left = p.right = p.parent = null; } root = (p.red) ? r : balanceDeletion(r, replacement); if (p == replacement) { // detach pointers TreeNode<K,V> pp; if ((pp = p.parent) != null) { if (p == pp.left) pp.left = null; else if (p == pp.right) pp.right = null; p.parent = null; } } } finally { unlockRoot(); } assert checkInvariants(root); return false; } /* ------------------------------------------------------------ */ // 以下是紅黑樹的經典算法 static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<K,V> p) { TreeNode<K,V> r, pp, rl; if (p != null && (r = p.right) != null) { if ((rl = p.right = r.left) != null) rl.parent = p; if ((pp = r.parent = p.parent) == null) (root = r).red = false; else if (pp.left == p) pp.left = r; else pp.right = r; r.left = p; p.parent = r; } return root; } static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p) { TreeNode<K,V> l, pp, lr; if (p != null && (l = p.left) != null) { if ((lr = p.left = l.right) != null) lr.parent = p; if ((pp = l.parent = p.parent) == null) (root = l).red = false; else if (pp.right == p) pp.right = l; else pp.left = l; l.right = p; p.parent = l; } return root; } static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x) { x.red = true; for (TreeNode<K,V> xp, xpp, xppl, xppr;;) { if ((xp = x.parent) == null) { x.red = false; return x; } else if (!xp.red || (xpp = xp.parent) == null) return root; if (xp == (xppl = xpp.left)) { if ((xppr = xpp.right) != null && xppr.red) { xppr.red = false; xp.red = false; xpp.red = true; x = xpp; } else { if (x == xp.right) { root = rotateLeft(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } if (xp != null) { xp.red = false; if (xpp != null) { xpp.red = true; root = rotateRight(root, xpp); } } } } else { if (xppl != null && xppl.red) { xppl.red = false; xp.red = false; xpp.red = true; x = xpp; } else { if (x == xp.left) { root = rotateRight(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } if (xp != null) { xp.red = false; if (xpp != null) { xpp.red = true; root = rotateLeft(root, xpp); } } } } } } static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root, TreeNode<K,V> x) { for (TreeNode<K,V> xp, xpl, xpr;;) { if (x == null || x == root) return root; else if ((xp = x.parent) == null) { x.red = false; return x; } else if (x.red) { x.red = false; return root; } else if ((xpl = xp.left) == x) { if ((xpr = xp.right) != null && xpr.red) { xpr.red = false; xp.red = true; root = rotateLeft(root, xp); xpr = (xp = x.parent) == null ? null : xp.right; } if (xpr == null) x = xp; else { TreeNode<K,V> sl = xpr.left, sr = xpr.right; if ((sr == null || !sr.red) && (sl == null || !sl.red)) { xpr.red = true; x = xp; } else { if (sr == null || !sr.red) { if (sl != null) sl.red = false; xpr.red = true; root = rotateRight(root, xpr); xpr = (xp = x.parent) == null ? null : xp.right; } if (xpr != null) { xpr.red = (xp == null) ? false : xp.red; if ((sr = xpr.right) != null) sr.red = false; } if (xp != null) { xp.red = false; root = rotateLeft(root, xp); } x = root; } } } else { // symmetric if (xpl != null && xpl.red) { xpl.red = false; xp.red = true; root = rotateRight(root, xp); xpl = (xp = x.parent) == null ? null : xp.left; } if (xpl == null) x = xp; else { TreeNode<K,V> sl = xpl.left, sr = xpl.right; if ((sl == null || !sl.red) && (sr == null || !sr.red)) { xpl.red = true; x = xp; } else { if (sl == null || !sl.red) { if (sr != null) sr.red = false; xpl.red = true; root = rotateLeft(root, xpl); xpl = (xp = x.parent) == null ? null : xp.left; } if (xpl != null) { xpl.red = (xp == null) ? false : xp.red; if ((sl = xpl.left) != null) sl.red = false; } if (xp != null) { xp.red = false; root = rotateRight(root, xp); } x = root; } } } } } /** * 遞歸檢查,確保構造的是正確無誤的紅黑樹 */ static <K,V> boolean checkInvariants(TreeNode<K,V> t) { TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right, tb = t.prev, tn = (TreeNode<K,V>)t.next; if (tb != null && tb.next != t) return false; if (tn != null && tn.prev != t) return false; if (tp != null && t != tp.left && t != tp.right) return false; if (tl != null && (tl.parent != t || tl.hash > t.hash)) return false; if (tr != null && (tr.parent != t || tr.hash < t.hash)) return false; if (t.red && tl != null && tl.red && tr != null && tr.red) return false; if (tl != null && !checkInvariants(tl)) return false; if (tr != null && !checkInvariants(tr)) return false; return true; } // Unsafe相關的初始化工做 private static final sun.misc.Unsafe U; private static final long LOCKSTATE; static { try { U = sun.misc.Unsafe.getUnsafe(); Class<?> k = TreeBin.class; LOCKSTATE = U.objectFieldOffset (k.getDeclaredField("lockState")); } catch (Exception e) { throw new Error(e); } } }
static final class ReservationNode<K,V> extends Node<K,V> { ReservationNode() { super(RESERVED, null, null, null); } Node<K,V> find(int h, Object k) { return null; } }
首先介紹一些基本的方法,這些方法不會直接用到,但倒是理解ConcurrentHashMap常見方法前提,由於這些方法被ConcurrentHashMap常見的方法調用。而後在介紹完這些基本方法的基礎上,再分析常見的containsValue、put、remove等常見方法。ide
private final Node<K,V>[] initTable() { Node<K,V>[] tab; int sc; while ((tab = table) == null || tab.length == 0) { if ((sc = sizeCtl) < 0) //真正的初始化是要禁止併發的,保證tables數組只被初始化一次,但又不能切換線程,因此須要yield()讓出CPU Thread.yield(); // lost initialization race; just spin else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { //更新sizeCtl標識爲初始化狀態 try { //若是當前表爲空,初始化table表 if ((tab = table) == null || tab.length == 0) { int n = (sc > 0) ? sc : DEFAULT_CAPACITY; @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = tab = nt; sc = n - (n >>> 2); //設置閾值爲總長度的0.75,從而可看出loadFactor沒有用到 } } finally { sizeCtl = sc; //設置閾值 } break; } } return tab; }
//volatile讀取table[i] 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); } //CAS更新table[i],更新Node鏈表的頭節點,或者TreeBin節點 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); } //volatile寫入table[i] static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) { U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v); } //嘗試將鏈表轉化爲紅黑樹 private final void treeifyBin(Node<K,V>[] tab, int index) { Node<K,V> b; int n, sc; if (tab != null) { //當table的length小於64時,只進行一次擴容 if ((n = tab.length) < MIN_TREEIFY_CAPACITY) tryPresize(n << 1); //將鏈表轉化爲紅黑樹 else if ((b = tabAt(tab, index)) != null && b.hash >= 0) { synchronized (b) { if (tabAt(tab, index) == b) { TreeNode<K,V> hd = null, tl = null; for (Node<K,V> e = b; e != null; e = e.next) { TreeNode<K,V> p = new TreeNode<K,V>(e.hash, e.key, e.val, null, null); if ((p.prev = tl) == null) hd = p; else tl.next = p; tl = p; } setTabAt(tab, index, new TreeBin<K,V>(hd)); } } } } } //將紅黑樹轉化爲鏈表,在調用此方法時synchronized加鎖,這裏再也不須要加鎖 static <K,V> Node<K,V> untreeify(Node<K,V> b) { Node<K,V> hd = null, tl = null; for (Node<K,V> q = b; q != null; q = q.next) { Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null); if (tl == null) hd = p; else tl.next = p; tl = p; } return hd; }
int n = tab.length, stride; if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE; // 最小值爲16
計算完成以後每一個transfer按照計算的值處理相應下標位置的桶,擴容操做從舊數組的末尾向前一次對hash桶進行處理。從末尾向前處理主要是減小和遍歷數據時的鎖衝突。從舊數組的末尾向前代碼以下:
//標記一個transfer任務是否完成,完成爲true,不然爲false boolean advance = true; //標記整個擴容任務是否完成 boolean finishing = false; // to ensure sweep before committing nextTab //僅截取部分代碼片斷,其中i表示當前transfer處理的hash桶的index,而bound表示當前transfer須要處理的hash桶的index的下界 while (advance) { int nextIndex, nextBound; if (--i >= bound || finishing) //代表一次transfer未執行完畢 advance = false; else if ((nextIndex = transferIndex) <= 0) { //transfer任務完成,能夠準備退出擴容 i = -1; advance = false; } //嘗試申請transfer任務 else if (U.compareAndSwapInt (this, TRANSFERINDEX, nextIndex, nextBound = (nextIndex > stride ? nextIndex - stride : 0))) { bound = nextBound; //transfer任務中hash桶的下界 i = nextIndex - 1; //transfer當前處理的hash桶的index advance = false; } }
擴容部分的完整代碼以下:
//x表示擴容須要增長的值 //check表示計數操做是否會觸發擴容,check<0表示不會觸發 //check<=1說明線程更新計數時沒有遇到競爭 private final void addCount(long x, int check) { CounterCell[] as; long b, s; if ((as = counterCells) != null || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { CounterCell a; long v; int m; boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) { fullAddCount(x, uncontended); return; } if (check <= 1) return; s = sumCount(); } if (check >= 0) { //檢測是否擴容 Node<K,V>[] tab, nt; int n, sc; //擴容基本條件 while (s >= (long)(sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) { int rs = resizeStamp(n); //計算本次擴容生成戳 if (sc < 0) { //代表此時沒有其餘線程擴容 //5個條件只要有一個爲true,則當前線程不能幫助擴容 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; //前5個條件都爲false時嘗試這次擴容,將正在執行transfer任務的線程數+1 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); } //嘗試讓當前線程成爲第一個執行transfer任務的線程 else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); //執行擴容 s = sumCount(); //從新計數看是否須要下一次擴容 } } } /** * Helps transfer if a resize is in progress. * 若是正在進行擴容,則嘗試幫助執行transfer任務 */ final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) { Node<K,V>[] nextTab; int sc; //判斷是否仍然在執行擴容 if (tab != null && (f instanceof ForwardingNode) && (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) { int rs = resizeStamp(tab.length); //計算擴容生成戳 //再次判斷是否正在執行擴容 while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) { // 判斷下是否能真正幫助這次擴容 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || transferIndex <= 0) break; //不能幫助則終止 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) { transfer(tab, nextTab); //不然執行這次擴容 break; } } return nextTab; //返回擴容後的數組 } return table; //若是是返回table說明擴容已經結束,table被其它線程賦值新數組 } //預先擴容,包含初始化邏輯的擴容 //用於putAll,此時是須要考慮初始化;鏈表轉化爲紅黑樹中,不知足table容量條件時,進行一次擴容,此時就是普通的擴容 private final void tryPresize(int size) { int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1); int sc; while ((sc = sizeCtl) >= 0) { Node<K,V>[] tab = table; int n; if (tab == null || (n = tab.length) == 0) { //用於處理初始化,跟initTable方法相同 n = (sc > c) ? sc : c; if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { try { if (table == tab) { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = nt; sc = n - (n >>> 2); } } finally { sizeCtl = sc; } } } // c <= sc,說明已經被擴容過了;n >= MAXIMUM_CAPACITY說明table數組已經到了最大長度 else if (c <= sc || n >= MAXIMUM_CAPACITY) break; else if (tab == table) { //能夠進行擴容 int rs = resizeStamp(n); if (sc < 0) { Node<K,V>[] nt; if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } } } // 執行節點遷移,準確地說是遷移內容,由於不少節點都須要進行復制,複製可以保證讀操做盡可能不受影響 private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) { int n = tab.length, stride; if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE; //計算每一個transfer負責處理多少個hash桶 if (nextTab == null) { //初始化Node數組 try { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1]; nextTab = nt; } catch (Throwable ex) { // try to cope with OOME sizeCtl = Integer.MAX_VALUE; return; } nextTable = nextTab; transferIndex = n; } int nextn = nextTab.length; // 轉發節點,在舊數組的一個hash桶中全部節點都被遷移完後,放置在這個hash桶中,代表已經遷移完,對它的讀操做會轉發到新數組 ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab); boolean advance = true; boolean finishing = false; //標識擴容工做是否完成 for (int i = 0, bound = 0;;) { Node<K,V> f; int fh; while (advance) { int nextIndex, nextBound; if (--i >= bound || finishing) // 一次transfer還未執行完畢 advance = false; else if ((nextIndex = transferIndex) <= 0) { // transfer任務已經沒有了,代表能夠準備退出擴容了 i = -1; advance = false; } //嘗試申請transfer任務 else if (U.compareAndSwapInt (this, TRANSFERINDEX, nextIndex, nextBound = (nextIndex > stride ? nextIndex - stride : 0))) { // transfer申請到任務後標記本身的任務區間 bound = nextBound; i = nextIndex - 1; advance = false; } } //處理擴容重疊 if (i < 0 || i >= n || i + n >= nextn) { int sc; if (finishing) { //擴容完成 nextTable = null; table = nextTab; sizeCtl = (n << 1) - (n >>> 1); return; } // 嘗試把正在執行擴容的線程數減1,代表本身要退出擴容 if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) { // 判斷下本身是否是本輪擴容中的最後一個線程,若是不是,則直接退出。 if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT) return; finishing = advance = true; //最後一個擴容的線程要從新檢查一次舊數組的全部hash桶,看是不是都被正確遷移到新數組了。 // 正常狀況下,從新檢查時,舊數組全部hash桶都應該是轉發節點,此時這個從新檢查的工做很快就會執行完。 // 特殊狀況,好比擴容重疊,那麼會有線程申請到了transfer任務,可是參數錯誤(舊數組和新數組對不上,不是2倍長度的關係), // 此時這個線程領取的任務會做廢,那麼最後檢查時,還要處理由於做廢二沒有被遷移的hash桶,把它們正確遷移到新數組中 i = n; // recheck before commit } } else if ((f = tabAt(tab, i)) == null) // hash桶自己爲null,不用遷移,直接嘗試安放一個轉發節點 advance = casTabAt(tab, i, null, fwd); else if ((fh = f.hash) == MOVED) //當前hash桶有線程在對其擴容 advance = true; // already processed else { synchronized (f) { //給f加鎖 // 判斷下加鎖的節點仍然是hash桶中的第一個節點,加鎖的是第一個節點纔算加鎖成功 if (tabAt(tab, i) == f) { Node<K,V> ln, hn; if (fh >= 0) { int runBit = fh & n; //記錄當前hash值的第X(Math.pow(2,X)=n)位的值 Node<K,V> lastRun = f; for (Node<K,V> p = f.next; p != null; p = p.next) { int b = p.hash & n; if (b != runBit) { runBit = b; lastRun = p; } } if (runBit == 0) { ln = lastRun; hn = null; } else { hn = lastRun; ln = null; } for (Node<K,V> p = f; p != lastRun; p = p.next) { int ph = p.hash; K pk = p.key; V pv = p.val; if ((ph & n) == 0) ln = new Node<K,V>(ph, pk, pv, ln); else hn = new Node<K,V>(ph, pk, pv, hn); } setTabAt(nextTab, i, ln); // 放在新table的hash桶中 setTabAt(nextTab, i + n, hn); // 放在新table的hash桶中 setTabAt(tab, i, fwd); // 把舊table的hash桶中放置轉發節點,代表此hash桶已經被處理 advance = true; } // 紅黑樹的狀況,先使用鏈表的方式遍歷,複製全部節點,根據高低位 //組裝成兩個鏈表lo和hi,而後看下是否須要進行紅黑樹變換,最後放在新數組對應的hash桶中 else if (f instanceof TreeBin) { TreeBin<K,V> t = (TreeBin<K,V>)f; TreeNode<K,V> lo = null, loTail = null; TreeNode<K,V> hi = null, hiTail = null; int lc = 0, hc = 0; for (Node<K,V> e = t.first; e != null; e = e.next) { int h = e.hash; TreeNode<K,V> p = new TreeNode<K,V> (h, e.key, e.val, null, null); //當前節點的hash值第X位爲0 if ((h & n) == 0) { if ((p.prev = loTail) == null) lo = p; else loTail.next = p; loTail = p; ++lc; } //當前節點的hash值第X位爲1 else { if ((p.prev = hiTail) == null) hi = p; else hiTail.next = p; hiTail = p; ++hc; } } //若是lo的size(lc)小於6,則將lo轉化爲鏈表 //若是lo的size大於6且hi的size(hc)不等於0,從新構造紅黑樹,若是hi的size爲0,則ln爲原始紅黑樹 ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : (hc != 0) ? new TreeBin<K,V>(lo) : t; //hn的設置桶ln相同 hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : (lc != 0) ? new TreeBin<K,V>(hi) : t; setTabAt(nextTab, i, ln); setTabAt(nextTab, i + n, hn); setTabAt(tab, i, fwd); advance = true; } } } } } }
以下是一個鏈表擴容的示意圖,第一張是一個hash桶中的一條鏈表,其中藍色節點表示第X位爲0,而紅色表示第X位爲1,擴容後舊table[i]的桶中爲一個ForwardingNode節點,而新nextTab[i]和nextTable[i+n]的桶中分別爲第二張和第三張圖。
static class Traverser<K,V> { Node<K,V>[] tab; // current table; updated if resized 擴容完成後的舊數組 Node<K,V> next; // the next entry to use 擴容完成後的新數組 TableStack<K,V> stack, spare; //存儲遍歷到的 ForwardingNodes int index; // index of bin to use next 下一個要讀取的hash桶的下標 int baseIndex; // current index of initial table 起始下標 int baseLimit; // index bound for initial table 終止下標 final int baseSize; // initial table size tab數組長度 Traverser(Node<K,V>[] tab, int size, int index, int limit) { this.tab = tab; this.baseSize = size; this.baseIndex = this.index = index; this.baseLimit = limit; this.next = null; } /** * Advances if possible, returning next valid node, or null if none. * 遍歷器指針移動到下一個有實際數據的節點,並返回該節點,若是結束則返回null */ final Node<K,V> advance() { Node<K,V> e; if ((e = next) != null) e = e.next; for (;;) { Node<K,V>[] t; int i, n; // must use locals in checks if (e != null) return next = e; //節點非空則直接返回該節點 //達到邊界條件直接返回null if (baseIndex >= baseLimit || (t = tab) == null || (n = t.length) <= (i = index) || i < 0) return next = null; //處理特殊節點(ForwardingNode、TreeBin、ReservationNode) if ((e = tabAt(t, i)) != null && e.hash < 0) { if (e instanceof ForwardingNode) { //遍歷ForwardingNode的nextTable tab = ((ForwardingNode<K,V>)e).nextTable; e = null; pushState(t, i, n); //將當前位置入棧 continue; } else if (e instanceof TreeBin) e = ((TreeBin<K,V>)e).first; else e = null; } if (stack != null) recoverState(n); //棧不爲空,出棧 else if ((index = i + baseSize) >= n) //棧爲空,遍歷下一個hash桶 index = ++baseIndex; // visit upper slots if present } } /** * Saves traversal state upon encountering a forwarding node. * 入棧操做,保存當前對tab的遍歷信息 */ private void pushState(Node<K,V>[] t, int i, int n) { TableStack<K,V> s = spare; // reuse if possible if (s != null) spare = s.next; else s = new TableStack<K,V>(); s.tab = t; s.length = n; s.index = i; s.next = stack; stack = s; } /** * Possibly pops traversal state. * 參數n爲當前tab數組的長度 * 可能會出棧,不出棧時,更改索引,準備遍歷的是FN.nextTable中對應的第二個hash桶 */ private void recoverState(int n) { TableStack<K,V> s; int len; while ((s = stack) != null && (index += (len = s.length)) >= n) { n = len; index = s.index; tab = s.tab; s.tab = null; TableStack<K,V> next = s.next; s.next = spare; // save for reuse stack = next; spare = s; } if (s == null && (index += baseSize) >= n) index = ++baseIndex; } }
public boolean containsValue(Object value) { if (value == null) throw new NullPointerException(); Node<K,V>[] t; if ((t = table) != null) { Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length); for (Node<K,V> p; (p = it.advance()) != null; ) { V v; if ((v = p.val) == value || (v != null && value.equals(v))) return true; } } return false; }
public boolean containsKey(Object key) { return get(key) != null; } 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) //當hash值小於0時,說明當前節點爲特殊節點,則以當前節點爲根節點進行遍歷,而不是遍歷該節點的next節點 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; }
public V put(K key, V value) { return putVal(key, value, false); } final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); //鍵或值存在null時直接拋出空指針異常 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) tab = initTable(); //初始化table else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) break; // no lock when adding to empty bin } else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); //發現轉發節點,幫助擴容 else { V oldVal = null; synchronized (f) { if (tabAt(tab, i) == f) { if (fh >= 0) { //當前hash值大於0說明hash桶中爲鏈表 binCount = 1; for (Node<K,V> e = f;; ++binCount) { K ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; //若是當前鍵值對存在,則更新value爲最新的value值 if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; if ((e = e.next) == null) { pred.next = new Node<K,V>(hash, key, value, null); break; } } } else if (f instanceof TreeBin) { //hash桶值爲紅黑樹 Node<K,V> p; binCount = 2; if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } } } if (binCount != 0) { //若是當前hash桶中的size大於8,將該鏈表轉化爲紅黑樹 if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); if (oldVal != null) return oldVal; break; } } } addCount(1L, binCount); //計數值加1 return null; }
public V remove(Object key) { return replaceNode(key, null, null); } final V replaceNode(Object key, V value, Object cv) { int hash = spread(key.hashCode()); for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; if (tab == null || (n = tab.length) == 0 || (f = tabAt(tab, i = (n - 1) & hash)) == null) break; //當前要移除的key不在table中 else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); else { V oldVal = null; boolean validated = false; synchronized (f) { if (tabAt(tab, i) == f) { if (fh >= 0) { //hash桶中爲鏈表 validated = true; for (Node<K,V> e = f, pred = null;;) { K ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { V ev = e.val; if (cv == null || cv == ev || (ev != null && cv.equals(ev))) { oldVal = ev; if (value != null) //若是當前value不爲空,則更新value e.val = value; else if (pred != null) //value爲空,則刪除該節點 pred.next = e.next; else setTabAt(tab, i, e.next); //刪除的是hash的第一個Node } break; } pred = e; if ((e = e.next) == null) break; } } else if (f instanceof TreeBin) { //hash桶爲紅黑樹 validated = true; TreeBin<K,V> t = (TreeBin<K,V>)f; TreeNode<K,V> r, p; if ((r = t.root) != null && (p = r.findTreeNode(hash, key, null)) != null) { V pv = p.val; if (cv == null || cv == pv || (pv != null && cv.equals(pv))) { oldVal = pv; if (value != null) p.val = value; else if (t.removeTreeNode(p)) //處理退化爲鏈表的狀況 setTabAt(tab, i, untreeify(t.first)); } } } } } //由於該方法多是執行替換也多是刪除,若是是刪除操做則計數值減1 if (validated) { if (oldVal != null) { if (value == null) addCount(-1L, -1); return oldVal; } break; } } } return null; }
至此ConcurrentHashMap的主要方法也就介紹完了,綜合比較Hashtable和ConcurrentHashMap,二者都是線程安全的,可是Hashtable是表級鎖,而ConcurrentHashMap是段級鎖,鎖住的單個Node,並且ConcurrentHashMap能夠併發讀取。對整張表進行迭代時,ConcurrentHashMap使用了不一樣於Hashtable的迭代方式,而是一種弱一致性的迭代器。