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)
我的问题是: 为什么我们要做“(重试&amp; 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次更改一样。
上述说明是否正确?
答案 0 :(得分:2)
让我们打破这个:
1:
(retries & 1) == 0
对于奇数,返回1,对于偶数,返回0。基本上,要想过去,如果数字是偶数的话,那就有1比2的机会。
2:
f = entryForHash(this, hash)
f是一个临时变量,用于存储段中最新条目的值。
3:
(/* ... */) != first
检查值是否已更改。如果是,它会将当前条目移动到开始,并再次重新迭代链接的节点以尝试获取锁定。
答案 1 :(得分:0)
我在并发利益邮件列表上问了这个问题,作者(Doug Lea)自己回答说:
是。我们只需要确保最终检测到陈旧性。 交替检查头部检查工作正常,并简化了使用 单处理器和多处理器的代码相同。
所以我认为这是这个问题的结束。
答案 2 :(得分: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.最终,该节点成为新的头,因此最终,我们放置的所有内容都将成为条目列表的头,并且在并发环境中工作时,它可能在同一条目列表中出现两个等于条目。
这是我的看法,我不确定它是否正确。所以我希望大家都给我一些建议,非常感谢!