【JDK1.8源码】ConcurrentHashMap并发容器图解

目录

1.类图对比HashMap

2.并发实现

2.1 内部对象安全发布

2.2 并发读写

2.2.1 get读

2.2.2 put


1.类图对比HashMap

数据结构与HashMap类似,详情请戳,在HashMap的基础上在其API接口上进行了并发访问控制等实现,使其变得线程安全。

ConcurrentHashMap(CHM) HashMap(HM)

2.并发实现

2.1 内部对象安全发布

数据结构与HashMap对比可以发现table是大体相似的,而entrySet等返回的都是View的视图包装类。

防止直接发布内部容器,被意外修改,导致Map的不变式约束被破坏,数据不一致不完整。

读写操作都做了重写,当对entrySetView等进行修改时,会同步修改this map,不会存在意外修改导致的数据不一致状况。具体请见下节

2.2 并发读写

2.2.1 get读

由于是读操作,实现的最简单,通过volatile语义保证桶里头元素的可见性,但是如果后面的链被修改可能不是最新的值。

    /*
     * 大部分与HashMap实现相似
     * 重点是tabAt和e.find 方法
     */
    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) {//找到对应桶下标对应第一个Node
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek))) //头上正好命中
                    return e.val;
            } 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;
    }

其中有个很重要的方法tabAt方法,通过Unsafe U去的getObjectVolatile方法,volatile语义保证可见性和有序性!

    //读取 sun.misc.Unsafe U,单例,通过JNI调用
    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);
    }

2.2.2 put

以下是put的核心方法,比HashMap增加了并发控制:

  1. 无锁CAS:casTabAt通过Unsafe U的CAS(无锁化)操作插入到空桶,负载较小且散列度较高的情况下极大提升效率(比互斥锁)
  2. volatile:轻量级锁保证可见性同时提升并发
  3. 锁细化:不是空桶的情况下,使用桶里的头元素的内部monitor锁(synchronized保护桶后面链着的整个链表或者红黑树!比起锁住整个map,仅锁住一个桶,锁粒度大大减小!,提升了并发度。
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException(); // 空校验
    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();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                break;                   // 空桶无锁CAS技术抢头位置
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) { // 同步在当前桶上链头
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        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;
                                if (!onlyIfAbsent)
                                    e.val = value; // key同,覆盖
                                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) { // 树节点
                        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) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i); // 转换树
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}
    //CAS替换
    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);
    }
    // set 写入
    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

Unsafe采用单例模式,是JDK实现的底层核心类(rt.jar),其方法通过JNI直接调用操作系统接口操作内存。

发布了119 篇原创文章 · 获赞 108 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/105067288