这是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;
}
假设当前有两个线程,A
和B
,并且当A
执行step1
时,它得到true
,但同时B
还将执行step1
并获得true
。 A
和B
都执行step2
。
在这种情况下,B
的{{1}}代替了Node
的{{1}},或者说A
的数据被{{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作为更改值的条件。