读不加锁,写加锁,CAS + synchronized

首先,如果 Node 数组为空,则调用 initTable 方法初始化 Node 数组,同时计算 key 的 hash 值,并定位到 Node 里的对应位置。

如果当前 Node 位置为空,即无冲突,则以 CAS 方式插入,多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,可以再次进行尝试。

如果当前 Node 位置不为空,则对该 Node 加 synchronized 锁,并加入到该 Node 所指向的链表里,对应代码如下所示。

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;
                    break;
                }
                Node<K,V> pred = e;
                if ((e = e.next) == null) {
                    pred.next = new Node<K,V>(hash, key, value);
                    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;
            }
        }
        else if (f instanceof ReservationNode)
            throw new IllegalStateException("Recursive update");
    }
}

ConcurrentHashMap是如何保证线程安全的