Map的实现类ConcurrentHashMap各种方法源码解析

  1. map.put() 方法解析

        //map.put 方法
        // final V putVal(K key, V value, boolean onlyIfAbsent) {}
        public V put(K key, V value) {
            return putVal(key, value, false);//put方法实现
        }
        
        // putVal() 方法
        final V putVal(K key, V value, boolean onlyIfAbsent) {
            // key 和 value 不能为null
            if (key == null || value == null) throw new NullPointerException();
            // spread() 方法 : return (h ^ (h >>> 16)) & HASH_BITS;
            // 高16位与低16位异或运算 例: 211221 = 211222^3 
            // 然后和 HASH_BITS 位与运算 例: ① 211221 = 211221 & 2147483647
            //                              ② 0 = 2147483648 & 2147483647  
            int hash = spread(key.hashCode());// 异或 与 运算得到的hash值
            int binCount = 0;// 初始化定义binCount
            for (Node<K,V>[] tab = table;;) {
                Node<K,V> f; int n, i, fh;
                if (tab == null || (n = tab.length) == 0)
                    tab = initTable();//执行初始化操作 初始化大小为16
                //通过哈希计算出一个表中的位置因为n是数组的长度,所以(n-1)&hash肯定不会出现数组越界
                else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                        //如果这个位置没有元素的话,则通过cas的方式尝试添加,注意这个时候是没有加锁的
                        if (casTabAt(tab, i, null,
                                 new Node<K,V>(hash, key, value, null)))
                        break;                   // no lock when adding to empty bin
                }
                // MOVED(-1) 值是 -1
                else if ((fh = f.hash) == MOVED)
                    tab = helpTransfer(tab, f);// 当前Map在扩容,先协助扩容,在更新值。
                else {
                    V oldVal = null;
                    // sysnchronized(线程安全)直接操作内存,对比lock性能兼容优越
                    // sysnchronized锁住的是括号中的对象,并不是代码块,如果对象操作冲突就会进入等待
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            // fh 是 f 的 hash 值,f 是在内存中做了一个对比之后的值
                            // 当前hash存储位置已经存在key值 执行覆盖或者末尾添加操作
                            if (fh >= 0) {
                                binCount = 1;
                                for (Node<K,V> e = f;; ++binCount) {
                                    K ek;
                                    // 判断是否存在重复的key 更新oldVal
                                    if (e.hash == hash &&
                                        ((ek = e.key) == key ||
                                         (ek != null && key.equals(ek)))) {
                                        oldVal = e.val;
                                        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;
                                    }
                                }
                            }
                            // 判断是否是treeBin 操作 执行红黑树的add操作
                            else if (f instanceof TreeBin) {
                                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) {
                        // 链表存储长度大于8 执行红黑树add操作
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (oldVal != null)
                            return oldVal;
                        break;
                    }
                }
            }
            // 统计节点个数,如果在8个以上的话,则会调用treeifyBin方法,来尝试转化为树,或者是扩容
            // 引用地址:http://www.cnblogs.com/zerotomax/p/8687425.html
            addCount(1L, binCount);
            return null;
        }
    
        // initTable() 初始化操作
        private final Node<K,V>[] initTable() {
            Node<K,V>[] tab; int sc;
            //第一次put的时候,table还没被初始化,进入while
            while ((tab = table) == null || tab.length == 0) {
                //sizeCtl初始值为0,当小于0的时候表示在别的线程在初始化表或扩展表
                if ((sc = sizeCtl) < 0)
                    Thread.yield(); // lost initialization race; just spin
                //SIZECTL:表示当前对象的内存偏移量,sc表示期望值,-1表示要替换的值,设定为-1表示要            
                //初始化表了
                else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                    try {
                        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;
                            // 位运算符 >>> : 位补零右移运算
                            // 负载系数 16 - (16 >>> 2) = 12 = 16 * 0.75
                            sc = n - (n >>> 2);
                        }
                    } finally {
                        // 最终一定执行 sizeCtl 的赋值
                        sizeCtl = sc;
                    }
                    break;
                }
            }
            return tab;
        }

     

  2. map.get() 方法解析

        // map.get() 方法
        public V get(Object key) {
            Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
            int h = spread(key.hashCode());// 得到key的hash值
            if ((tab = table) != null && (n = tab.length) > 0 &&
                // (n - 1) & h) 当前数组的长度与hash值 与运算 
                // 计算当前key 需要存储的位置 如果不是null 
                (e = tabAt(tab, (n - 1) & h)) != null) {
                // 如果存储的位置已经存在key
                if ((eh = e.hash) == h) {
                    // 如果key值是重复的 执行覆盖方法
                    if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                        return e.val;
                }
             //hash值为负值表示正在扩容,这个时候查的是ForwardingNode的find方法来定位到nextTable来
            //eh=-1,说明该节点是一个ForwardingNode,正在迁移,此时调用ForwardingNode的find方法去 
            //nextTable里找。
            //eh=-2,说明该节点是一个TreeBin,此时调用TreeBin的find方法遍历红黑树,由于红黑树有可能正 
            //在旋转变色,所以find里会有读写锁。
            //eh>=0,说明该节点下挂的是一个链表,直接遍历该链表即可。
                else if (eh < 0)
                    return (p = e.find(h, key)) != null ? p.val : null;
                while ((e = e.next) != null) {// 以上条件都不满足 继续循环查找
                    if (e.hash == h &&
                        ((ek = e.key) == key || (ek != null && key.equals(ek))))
                        return e.val;
                }
            }
            return null;
        }

     

  3. map.remove() 方法解析

        // map.remove() 方法
        public V remove(Object key) {
            return replaceNode(key, null, null);
        }
        
        // replaceNode() 方法
        final V replaceNode(Object key, V value, Object cv) {
            int hash = spread(key.hashCode());// 取当前key的hash值
            //循环操作 concurrentHashMap 当其他线程在执行扩容
            //操作时 可以协助处理, 处理完之后再来执行当前方法,提高扩容时copy效率
            for (Node<K,V>[] tab = table;;) {    
                Node<K,V> f; int n, i, fh;
                // 判断数据是否为null 或者通过计算得到的存放地址key为null直接结束循环
                if (tab == null || (n = tab.length) == 0 ||
                    (f = tabAt(tab, i = (n - 1) & hash)) == null)
                    break;
                else if ((fh = f.hash) == MOVED)// 判断是否有其他线程在执行扩容操作
                    tab = helpTransfer(tab, f);// 协助 执行 数据 Transfer 提高效率
                else {
                    V oldVal = null;
                    boolean validated = false;// 定义validated 为 false
                    // synchronized 锁括号中的对象 防止多个线程同时都操作这个对象 排队等候
                    synchronized (f) {
                        // 判断内存中是否存在 f 
                        if (tabAt(tab, i) == f) {
                            // fh 当前存储位置存在数据 并且数据书链表存储方式
                            if (fh >= 0) {
                                validated = true;
                                //循环查找当前链表数据
                                for (Node<K,V> e = f, pred = null;;) {
                                    K ek;
                                    // 判断是否是需要remove的数据
                                    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;// 保留oldVal 返回
                                            if (value != null)
                                                e.val = value;
                                            // 判断当前链表存储位置 的上一个node是否是null
                                            else if (pred != null)
                                                // 移动上一个node.next = e.next
                                                // remove e
                                                pred.next = e.next;
                                            else
                                                // 设置tab的当前i位置为e.next
                                                setTabAt(tab, i, e.next);
                                                
                                        }
                                        break;//结束循环
                                    }
                                    pred = e;//以上条件不满足 继续循环
                                    // 如果e.next 是null 结束循环
                                    if ((e = e.next) == null)
                                        break;
                                }
                            }
                            // 判断是否是红黑树节点类型  执行红黑树操作方法
                            else if (f instanceof TreeBin) {
                                validated = true;
                                TreeBin<K,V> t = (TreeBin<K,V>)f;//转换为treeBin类型操作
                                TreeNode<K,V> r, p;
                                // 判断树的根节点不为null 并且 根据hash和key得到的数据不为null
                                if ((r = t.root) != null &&
                                    (p = r.findTreeNode(hash, key, null)) != null) {
                                    // 定义pv
                                    V pv = p.val;
                                    if (cv == null || cv == pv ||
                                        (pv != null && cv.equals(pv))) {
                                        oldVal = pv;//保留oldVal 返回使用
                                        if (value != null)// value 传值一般都是null
                                            p.val = value;
                                        else if (t.removeTreeNode(p))//执行树的remove操作
                                                // 从新设置数组
                                                setTabAt(tab, i, untreeify(t.first));
                                    }
                                }
                            }
                        }
                    }
                    // 刚开始以为validated会造成死循环 经过分析这里本身就是一个循环,在validated为        
                    //false到这里的时候会继续循环,查找当前需要remove的Node,为null的情况下直接返回
                    if (validated) {
                        if (oldVal != null) {
                            if (value == null)
                                addCount(-1L, -1);
                            return oldVal;
                        }
                        break;
                    }
                }
            }
            return null;//没有匹配返回null
        }

     

  4. map.clear() 方法解析

        // map.clear() 方法
        public void clear() {
            long delta = 0L; // negative number of deletions
            int i = 0;
            Node<K,V>[] tab = table;
            // 循环执行数据的clear
            while (tab != null && i < tab.length) {
                int fh;
                Node<K,V> f = tabAt(tab, i);
                if (f == null)
                    ++i;
                else if ((fh = f.hash) == MOVED) {// 判断是否有线程在执行扩容操作 
                    tab = helpTransfer(tab, f);// 执行协助
                    i = 0; // restart
                }
                else {
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            Node<K,V> p = (fh >= 0 ? f :
                                           (f instanceof TreeBin) ?
                                           ((TreeBin<K,V>)f).first : null);
                            while (p != null) {
                                --delta;
                                p = p.next;
                            }
                            setTabAt(tab, i++, null);
                        }
                    }
                }
            }
            if (delta != 0L)
                addCount(delta, -1);
        }

     

 

hash table虽然性能上不如ConcurrentHashMap,但并不能完全被取代,两者的迭代器的一致性不同的,hash table的迭代器是强一致性的,而concurrenthashmap是弱一致的。 ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 
下面是大白话的解释: 
- Hashtable的任何操作都会把整个表锁住,是阻塞的。好处是总能获取最实时的更新,比如说线程A调用putAll写入大量数据,期间线程B调用get,线程B就会被阻塞,直到线程A完成putAll,因此线程B肯定能获取到线程A写入的完整数据。坏处是所有调用都要排队,效率较低。 
- ConcurrentHashMap 是设计为非阻塞的。在更新时会局部锁住某部分数据,但不会把整个表都锁住。同步读取操作则是完全非阻塞的。好处是在保证合理的同步前提下,效率很高。坏处 是严格来说读取操作不能保证反映最近的更新。例如线程A调用putAll写入大量数据,期间线程B调用get,则只能get到目前为止已经顺利插入的部分 数据。

选择哪一个,是在性能与数据一致性之间权衡。ConcurrentHashMap适用于追求性能的场景,大多数线程都只做insert/delete操作,对读取数据的一致性要求较低。
--------------------- 
原文:https://blog.csdn.net/programmer_at/article/details/79715177 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章