JAVA并发容器源码分析【二】ConcurrentHashMap分析之get

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

文章首发于:clawhub.club


先贴一段代码,根据源码分析ConcurrentHashMap中的get方法:

        /**
         * Returns the value to which the specified key is mapped,
         * or {@code null} if this map contains no mapping for the key.
         *
         * 返回指定键映射到的值,如果此映射不包含键的映射,则返回{@code null}。
         *
         * <p>More formally, if this map contains a mapping from a key
         * {@code k} to a value {@code v} such that {@code key.equals(k)},
         * then this method returns {@code v}; otherwise it returns
         * {@code null}.  (There can be at most one such mapping.)
         *
         * 更正式地说,如果这个映射包含一个键{@code k}到一个值{@code v}的映射,
         * 使得{@code key.equals(k)},那么这个方法返回{@code v};否则返回{@code null}。
         * (最多可以有一个这样的映射。)
         *
         * @throws NullPointerException if the specified key is null 如果指定的键为空
         */
        public V get(Object key) {
            Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
            //扩展key的Hash值
            int h = spread(key.hashCode());
            //如果表格不为空且表格长度大于0且所查key的所在节点不为空,(n - 1) & h相当于取模操作,即获取其索引位置。
            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;
                }
                //如果当前节点hash值小于0,溢出的时候为负数
                else if (eh < 0)
                    //调用find方法,返回值,或者没找到则返回空
                    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;
        }

节点Node

      /* ---------------- Nodes -------------- */

        /**
         * Key-value entry.  This class is never exported out as a
         * user-mutable Map.Entry (i.e., one supporting setValue; see
         * MapEntry below), but can be used for read-only traversals used
         * in bulk tasks.  Subclasses of Node with a negative hash field
         * are special, and contain null keys and values (but are never
         * exported).  Otherwise, keys and vals are never null.
         * 键值项。这个类永远不会作为用户可变的Map.Entry导出,但是可以用于批量任务中使用的只读遍历。
         * 具有负哈希字段的Node的子类是特殊的,包含空键和值(但从不导出)。否则,键和val永远不会为空。
         */
        static class Node<K,V> implements 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; }
            public final V setValue(V value) {
                throw new UnsupportedOperationException();
            }

            public final boolean equals(Object o) {
                Object k, v, u; Entry<?,?> e;
                return ((o instanceof Map.Entry) &&
                        (k = (e = (Entry<?,?>)o).getKey()) != null &&
                        (v = e.getValue()) != null &&
                        (k == key || k.equals(key)) &&
                        (v == (u = val) || v.equals(u)));
            }

            /**
             * Virtualized support for map.get(); overridden in subclasses.
             * 对map.get()的虚拟化支持;在子类覆盖。
             * h:待查找key的hash
             * k:待查找key
             */
            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;
            }
        }

从源码可以看出,ConcurrentHashMap中的get方法是没有锁的。
分析一下get方法是怎么实现的多线程下数据可见性。

  1. 通过tabAt的volatile读
  2. 通过结点Node的volatile属性next
  3. 通过结点Node的volatile属性val

JVM保证了volatile修饰的数据的内存可见性,后期专门学习一下volatile关键字。


来源:https://www.jianshu.com/p/a75d196731c0

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » JAVA并发容器源码分析【二】ConcurrentHashMap分析之get

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏