JDK8中ConcurrentHashMap源码解析

在介绍ConcurrentHashMap源码之前,首先需要了解以下几个知识

1、JDK1.8中ConcurrentHashMap的基本结构

2、并发编程的三个概念:可见性,原子性,有序性

3、CAS(CompareAndSwap):比较和交换,是原子性操作,属于乐观锁的一种实现。

4、java中的锁机制

先简单看下ConcurrentHashMap类在jdk1.8中的设计,由数组+链表+红黑树构成,其基本结构如图所示:

/**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
   * 数组结构。采用第一次插入式才初始化的懒惰模式。容量总是2的幂次方。可通过迭代直接访问。
*/ transient volatile Node<K,V>[] table; // volatile保证线程间可见
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash; 
        final K key;
        volatile V val;
        volatile Node<K,V> next;
}

接下来看一下初一些重要的初始化变量

/**
     * 初始化默认容量
     */
    private static final int DEFAULT_CAPACITY = 16; 
/**
   * 最大容量
*/ private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
     * 当容量达到当前最大容量的75%时,进行扩容操作
     */
    private static final float LOAD_FACTOR = 0.75f;
/**
     * 当链表长度>=8,转换为红黑树
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 当红黑树节点个数<=6,转换为链表
     */
    static final int UNTREEIFY_THRESHOLD = 6;
/**
 * 记录容器的容量大小,通过CAS更新
 */
private transient volatile long baseCount;

/**
 * 初始化和扩容控制参数。为负数时表示table正在被初始化或resize:-1(初始化),-(1+扩容线程数)
* sizeCtl默认值为0,大于0是扩容的阀值
*/ private transient volatile int sizeCtl;

最后我们从put方法正式进入源码分析

    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;
     // while(true)循环,不断的尝试,因为在table的初始化和casTabAt用到了compareAndSwapInt、compareAndSwapObject
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
       // 如果数组(HashMap)还未初始化,进行初始化操作(CAS操作)
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
       // 计算新插入数据在数组(HashMap)中的位置,如果该位置上的节点为null,直接放入数据(CAS操作)
            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
            }
       // 如果该节点的hash值为MOVED,说明正在进行扩容操作或者已经扩容未完成
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
       // 
            else {
                V oldVal = null;
                synchronized (f) {  // 对特定数组节点tab[i]加锁
                    if (tabAt(tab, i) == f) { // 判断tab[i]是否有变化
                        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)))) { // 如果新插入值和tab[i]处的hash值和key值一样,进行替换
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) { // 如果此节点为尾部节点,把此节点的next引用指向新数据节点
                                    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;
    }

上面代码已经关键部分已经做了注释,一些函数不再细讲。

下面对ConcurrentHashMap的扩容机制进行讲解

猜你喜欢

转载自www.cnblogs.com/kooker/p/9552853.html