Учитывая Javaslang/Vavr неизменяемую карту и функция, которая обновляет эту карту:
private Map<Foo, Bar> myMap = HashMap.empty();
public void setBar(Foo foo, Bar bar) {
myMap = myMap.put(foo, bar);
}
Как я могу гарантировать, что для двух одновременных вызовов setBar()
для разных ключей Foo
будут записаны их обновления?
// thread A
setBar(fooA, barA)
// thread B
setBar(fooB, barB)
Похоже, что существует риск того, что вызовы будут чередоваться так, что:
- поток A получает
{}
- поток B получает
{}
- поток B вычисляет
{}
+fooB -> barB
={(fooB -> barB)}
- поток B устанавливает
myMap
в{(fooB -> barB)}
- поток A вычисляет
{}
+fooA -> barA
={(fooA -> barA)}
- поток A устанавливает
myMap
в{(fooA -> barA)}
- обновление потока B потеряно
Используя AtomicReference
, я пришел к следующему, более или менее основанному на методах ConcurrentStack
в разделе «Неблокирующие алгоритмы» Java Concurrency in Практика.
private AtomicReference<Map<Foo, Bar>> myMap =
new AtomicReference<>(HashMap.empty());
public void setBar(Foo foo, Bar bar) {
Map<Foo, Bar> myMap0;
Map<Foo, Bar> myMap1;
do {
myMap0 = myMap.get();
myMap1 = myMap0.put(foo, bar);
} while (!myMap.compareAndSet(myMap0, myMap1));
}
Это правильно? И если да, то является ли это такой же хорошей реализацией, как я, вероятно, получу, или есть что-то более простое (например, какой-то Java 8 AtomicReference
API, который мне не хватает, который реализует этот шаблон)?