Unblockable atom upgrade to immutable map
Given a Javaslang / Vavr immutable map and a function that updates this map:
private Map<Foo, Bar> myMap = HashMap.empty();
public void setBar(Foo foo, Bar bar) {
myMap = myMap.put(foo, bar);
}
How can I ensure that two simultaneous calls setBar()
for different keys Foo
will both be marked for their updates?
// thread A
setBar(fooA, barA)
// thread B
setBar(fooB, barB)
There seems to be a risk that the calls will be interleaved in such a way that:
- thread A receives
{}
- stream B receives
{}
- stream B computes
{}
+fooB -> barB
={(fooB -> barB)}
- stream B sets
myMap
to{(fooB -> barB)}
- thread Computes
{}
+fooA -> barA
={(fooA -> barA)}
- thread A sets
myMap
to{(fooA -> barA)}
- stream B is updated.
Using AtomicReference
, I came up with the following, more or less based on the methods ConcurrentStack
in the "Non-Blocking Algorithms" section of Java Concurrency in Practice .
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));
}
It is right? And if so, is this a good implementation I can get, or is there something simpler (like some Java 8 AtomicReference
API I'm missing that implements this pattern)?
source to share
Usage AtomicReference
in this case is good. You can use the shortcut method
public void setBar(Foo foo, Bar bar) {
myMap.updateAndGet(map -> map.put(foo, bar)));
}
instead of this. See the javadoc forAtomicReference.updateAndGet
. The default java implementation is exactly the same as yours in Java 8.
source to share