Hashtable、ConcurrentHashMap源碼分析

Hashtable、ConcurrentHashMap源碼分析

  爲何把這兩個數據結構對比分析呢,相信你們都明白。首先兩者都是線程安全的,可是兩者保證線程安全的方式倒是不一樣的。廢話很少說了,從源碼的角度分析一下二者的異同,首先給出兩者的繼承關係圖。java

 Hashtable類屬性和方法源碼分析

  咱們仍是先給出一張Hashtable類的屬性和方法圖,其中Entry<K,V>是Hashtable類的靜態內部類,該類繼承自Map.Entry<K,V>接口。以下將會詳細講解Hashtable類中屬性和方法的含義。node

  • 屬性
  1. Entry<?,?>[] table :Entry類型的數組,用於存儲Hashtable中的鍵值對;
  2. int count :存儲hashtable中有多少個鍵值對
  3. int threshold :當count值大於該值是,哈希表擴大容量,進行rehash()
  4. float loadFactor :threshold=哈希表的初始大小*loadFactor,初始容量默認爲11,loadFactor值默認爲0.75
  5. int modCount :實現"fail-fast"機制,在併發集合中對Hashtable進行迭代操做時,若其餘線程對Hashtable進行結構性的修改,迭代器會經過比較expectedModCount和modCount是否一致,若是不一致則拋出ConcurrentModificationException異常。以下經過一個拋出ConcurrentModificationException異常的例子說明。
    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) */
    ConcurrentModificationException異常

    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();
                }
            }
        }
    Enumerator類
  • 方法
  1. contains(Object value),該方法是判斷該hashtable中是否含有值爲value的鍵值對,執行該方法須要加鎖(synchronized)。hashtable中不容許存儲空的value,因此當查找value爲空時,直接拋出空指針異常。接下來是兩個for循環遍歷table。由如上的Entry實體類中的屬性能夠看出,next屬性是指向與該實體擁有相同hashcode的下一個實體。
  2. containsKey(Object key),該方法是判斷該hashtable中是否含有鍵爲key的鍵值對,執行該方法也須要對整張table加鎖(synchronized)。首先根據當前給出的key值計算hashcode,並有hashcode值計算該key所在table數組中的下標,依次遍歷該下標中的每個Entry對象e。因爲不一樣的hashcode映射到數組中下標的位置可能相同,所以首先判斷e的hashcode值和所查詢key的hashcode值是否相同,若是相同在判斷key是否相等。
  3. get(Object key),獲取當前鍵key所對應的value值,本方法和containsKey(Object key)方法除了返回值其它都相同,若是能找到該key對應的value,則返回value的值,若是不能則返回null。數組

  4.  put(K key, V value),將該鍵值對加入table中。首先插入的value不能爲空。其次若是當前插入的key值已經在table中存在,則用新的value替換掉原來的value值,並將原來的value值做爲該方法的返回值返回。若是當前插入的key不在table中,則將該鍵值對插入。安全

    插入的方法首先判斷當前table中的值是否大於閾值(threshold),若是大於該閾值,首先對該表擴容,再將新的鍵值對插入table[index]的鏈表的第一個Entry的位置上。數據結構

  5. remove(Object key),將鍵爲key的Entry從table表中移除。一樣該方法也須要鎖定整個table表。若是該table中存在該鍵,則返回刪除的key的value值,若是當前table中不存在該key,則該方法的返回值爲null。
  6. replace(K key, V value),將鍵爲key的Entry對象值更新爲value,並將原來的value最爲該方法的返回值。多線程

ConcurrentHashMap類屬性和方法源碼分析

  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;
  •  基本類
  1. Node<K,V>:基本結點/普通節點。當table中的Entry以鏈表形式存儲時才使用,存儲實際數據。此類不會在ConcurrentHashMap之外被修改,並且該類的key和value永遠不爲null(其子類可爲null,隨後會介紹)。
    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;
            }
        }
    Node<K,V>
  2. TreeNode:紅黑樹結點。當table中的Entry以紅黑樹的形式存儲時纔會使用,存儲實際數據。ConcurrentHashMap中對TreeNode結點的操做都會由TreeBin代理執行。當知足條件時hash會由鏈表變爲紅黑樹,可是TreeNode中經過屬性prev依然保留鏈表的指針。
    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;
            }
        }
    TreeNode<K,V>
  3. ForwardingNode:轉發結點。該節點是一種臨時結點,只有在擴容進行中才會出現,其爲Node的子類,該節點的hash值固定爲-1,而且他不存儲實際數據。若是舊table的一個hash桶中所有結點都遷移到新的數組中,舊table就在桶中放置一個ForwardingNode。當讀操做或者迭代操做遇到ForwardingNode時,將操做轉發到擴容後新的table數組中去執行,當寫操做碰見ForwardingNode時,則嘗試幫助擴容。
    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;
                    }
                }
            }
        }
    ForwardingNode<K,V>

    補充圖一張說明擴容下是如何遍歷結點的。dom

  4. TreeBin:代理操做TreeNode結點。該節點的hash值固定爲-2,存儲實際數據的紅黑樹的根節點。由於紅黑樹進行寫入操做整個樹的結構可能發生很大變化,會影響到讀線程。所以TreeBin須要維護一個簡單的讀寫鎖,不用考慮寫-寫競爭的狀況。固然並非所有的寫操做都須要加寫鎖,只有部分put/remove須要加寫鎖。
    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);
                }
            }
        }
    TreeBin<K,V>
  5. ReservationNode:保留結點,也被稱爲空節點。該節點的hash值固定爲-3,不保存實際數據。正常的寫操做都須要對hash桶的第一個節點進行加鎖,若是hash桶的第一個節點爲null時是沒法加鎖的,所以須要new一個ReservationNode節點,做爲hash桶的第一個節點,對該節點進行加鎖。
    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;
            }
        }
    ReservationNode<K,V>
  • ConcurrentHashMap方法

  首先介紹一些基本的方法,這些方法不會直接用到,但倒是理解ConcurrentHashMap常見方法前提,由於這些方法被ConcurrentHashMap常見的方法調用。而後在介紹完這些基本方法的基礎上,再分析常見的containsValue、put、remove等常見方法。ide

  1. Node<K,V>[] initTable():初始化table的方法。初始化這個工做不是在構造函數中執行的,而是在put方法中執行,put方法中發現table爲null時,調用該方法。
    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;
        }
    initTable方法
  2. 以下幾個方法是用於讀取table數組,使用Unsafe提供更強的功能代替普通的讀寫。
    //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;
    }
    View Code
  3. 擴容方法:擴容分爲兩個步驟:第一步新建一個2倍大小的數組(單線程完成),第二步是rehash,把舊數組中的數據從新計算hash值放入新數組中。ConcurrentHashMap在第二步中處理舊table[index]中的節點時,這些節點要麼在新table[index]處,要麼在新table[index]和table[index+n]處,所以舊table各hash桶中的節點遷移不相互影響。ConcurrentHashMap擴容能夠在多線程下完成,所以就須要計算每一個線程須要負責處理多少個hash桶。
    int n = tab.length, stride;
            if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
                stride = MIN_TRANSFER_STRIDE; // 最小值爲16
    計算每一個transfer處理桶的個數

    計算完成以後每一個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;
        }
    }
    計算每一個transfer處理hash桶的區域

    擴容部分的完整代碼以下:

    //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]的桶中分別爲第二張和第三張圖。

  4. Traverser只讀遍歷器:確切的說它不是方法,而是一個內部類。ConcurrentHashMap的多線程擴容增長了對ConcurrentHashMap遍歷的困難。當遍歷舊table時,若是遇到某個hash桶中爲ForwardingNode節點,則遍歷順序參考基本類中ForwardingNode中的介紹。
    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;
            }
        }
    Traverser
  5. containsValue(Object value):遍歷ConcurrentHashMap看是否存在值爲value的Node。
    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;
        }
    containsValue(Object value)
  6. containsKey(Object key):遍歷ConcurrentHashMap看是否存在鍵爲key的Node。
    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;
    }
    containsKey(Object key)
  7. put(K key, V value):將該鍵值對插入ConcurrentHashMap中。
    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;
        }
    put(K key, V value)
  8. remove(Object key):刪除鍵爲key的Node。一樣其中也包含了對replace(Object key, V value, Object cv)的介紹。
    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;
        }
    remove(Object key)

  至此ConcurrentHashMap的主要方法也就介紹完了,綜合比較Hashtable和ConcurrentHashMap,二者都是線程安全的,可是Hashtable是表級鎖,而ConcurrentHashMap是段級鎖,鎖住的單個Node,並且ConcurrentHashMap能夠併發讀取。對整張表進行迭代時,ConcurrentHashMap使用了不一樣於Hashtable的迭代方式,而是一種弱一致性的迭代器。

相關文章
相關標籤/搜索