Вот часть кодов для метода 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.
в этой ситуации BNode заменяет ANode, или данные A заменяются на B, это неверно.
Я не знаю, правильно это или неправильно, может ли кто-нибудь помочь мне решить эту проблему?
Вот исходный код




Вот как реализован 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))
мы можем видеть, если предположить, что третий параметр compareAndSwapObject является ожидаемым значением, то, из-за гарантированной атомарности, поток A или Bкоторый сначала выполняет compareAndSwapObject увидит здесь null и compareAndSwapObjectфактически заменит объект, тогда как следующий поток, выполняющий compareAndSwapObject, не изменит значение, потому что фактическое значение больше не является нулевым, тогда как нулевое значение ожидалось как условие для изменения значения.
да, я знаю это, поэтому в этом случае это означает, что поток B не будет помещать значение в ConcurrentHashMap
Собственно нет: compareAndSwapObject, так же как и catTabAt возвращает истину, если замена была произведена. И учитывая, что if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) break; не будет вызывать break (потому что для второй операции записи он вернет false), цикл верхнего уровня (for (Node<K,V>[] tab = table;;) {...}) просто запустится еще раз.
Всегда добро пожаловать :) Если я вам помог - не забудьте отметить ответ как принятый.
Не могли бы вы дать ссылку на полный исходный код?