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