JDK7中的ConcurrentHashMap代码说明(scanAndLockForPut)

问题描述 投票:4回答:3

JDK7中ConcurrentHashMap中方法scanAndLockForPut的source codes说:

private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
    HashEntry<K,V> first = entryForHash(this, hash);
    HashEntry<K,V> e = first;
    HashEntry<K,V> node = null;
    int retries = -1; // negative while locating node
    while (!tryLock()) {
        HashEntry<K,V> f; // to recheck first below
        if (retries < 0) {
            if (e == null) {
                if (node == null) // speculatively create node
                    node = new HashEntry<K,V>(hash, key, value, null);
                retries = 0;
            }
            else if (key.equals(e.key))
                retries = 0;
            else
                e = e.next;
        }
        else if (++retries > MAX_SCAN_RETRIES) {
            lock();
            break;
        }
        else if ((retries & 1) == 0 &&
                 (f = entryForHash(this, hash)) != first) {
            e = first = f; // re-traverse if entry changed
            retries = -1;
        }
    }
    return node;
}

我理解代码的含义,但如果输入代码,我不知道的是其他内容:

else if ((retries & 1) == 0 && (f = entryForHash(this, hash)) != first)

我的问题是:为什么我们要做“(重试&1)== 0”?

编辑:我有点想通了。这都是因为常量MAX_SCAN_RETRIES:

static final int MAX_SCAN_RETRIES = Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;

在单核处理器中,MAX_SCAN_RETRIES = 1.因此第二次线程进入循环“while(tryLock)”时,不必检查第一个节点是否已更改。

但是,在多核处理器中,这将像检查第一个节点是否在while循环中每2次更改一样。

以上解释是否正确?

concurrency java.util.concurrent concurrenthashmap
3个回答
2
投票

让我们打破这个:

1:

(retries & 1) == 0

这对于奇数返回1,对于偶数返回0。基本上,要想过去,如果数字是偶数的话,那就有1比2的机会。

2:

f = entryForHash(this, hash)

f是一个临时变量,用于存储段中最新条目的值。

3:

(/* ... */) != first

检查值是否更改。如果是,则将当前条目移动到开始,并再次重新迭代链接的节点以尝试获取锁定。


0
投票

我在并发兴趣邮件列表上问了这个问题,作者(Doug Lea)自己回答说:

是。我们只需要确保最终检测到陈旧性。交替检查头部检查工作正常,并简化了对单处理器和多处理器使用相同代码的过程。

link

所以我认为这是这个问题的结束。


0
投票

我觉得这个方法有一些错误!首先让我们看看put方法:

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
        HashEntry<K,V> node = tryLock() ? null :
            scanAndLockForPut(key, hash, value);//1. scanAndLockForPut only return
                                                //   null or a new Entry
        V oldValue;
        try {
            HashEntry<K,V>[] tab = table;
            int index = (tab.length - 1) & hash;
            HashEntry<K,V> first = entryAt(tab, index);
            for (HashEntry<K,V> e = first;;) {
                if (e != null) {
                    K k;
                    if ((k = e.key) == key ||
                        (e.hash == hash && key.equals(k))) {
                        oldValue = e.value;
                        if (!onlyIfAbsent) {
                            e.value = value;
                            ++modCount;
                        }
                        break;
                    }
                    e = e.next;
                }
                else {
                    // 2. here the node is null or a new Entry
                    //    and the node.next is the origin head node 
                    if (node != null)
                        node.setNext(first);
                    else
                        node = new HashEntry<K,V>(hash, key, value, first);
                    int c = count + 1;
                    if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                        rehash(node);
                    else
                        setEntryAt(tab, index, node);//3. finally, the node become 
                                                     // the new head,so eventually 
                                                     // every thing we put will be 
                                                     // the head of the entry list
                                                     // and it may appears two equals
                                                     // entry in the same entry list.
                    ++modCount;
                    count = c;
                    oldValue = null;
                    break;
                }
            }
        } finally {
            unlock();
        }
        return oldValue;
    }

步骤:1。scanAndLockForPut仅返回null或新的Entry。

步骤:2。节点最终是一个新的Entry,而node.next是原始头节点

步骤:3。最后,节点成为新的头部,因此最终我们放置的每个东西都将是条目列表的头部,并且当concurrentHashMap在并发环境中工作时,它可能在同一条目列表中出现两个等于条目。

这是我的看法,我不确定它是否正确。所以我希望你们都给我一些建议,非常感谢!!

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.