关于ConcurrentHashMap的putVal方法的源代码的困惑

时间:2018-11-29 10:32:19

标签: java multithreading concurrenthashmap

这是putVal方法的代码的一部分:

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();  // lazy Initialization
        //step1,tabAt(...) is CAS
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            //step2,casTabAt(...) is CAS
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }
       ...
    return null;
}  

假设当前有两个线程,AB,并且当A执行step1时,它得到true,但同时B还将执行step1并获得trueAB都执行step2

enter image description here

在这种情况下,B的{​​{1}}代替了Node的{​​{1}},或者说A的数据被{{1 }},这是错误的。

我不知道这是对还是错,有人可以帮我解决吗?

1 个答案:

答案 0 :(得分:2)

casTabAt的实现方式如下:

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);
}

U声明如下:private static final sun.misc.Unsafe U;。此类的方法在较低级别上保证原子性。从这种用法:

casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))

我们可以看到assuming that the third parameter of compareAndSwapObject is expected value, that,由于原子性得到保证,首先执行A的{​​{1}}或B线程 将看到{{ 1}}和compareAndSwapObject 实际上将替换对象,而执行null的下一个线程不会更改该值,因为实际值不为null不再,而应该将null作为更改值的条件