问题
Given a Javaslang / Vavr immutable map, and a function that updates that 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 concurrent calls to setBar() for different Foo keys will both have their updates recorded?
// thread A
setBar(fooA, barA)
// thread B
setBar(fooB, barB)
It seems like there's a risk that the calls will be interleaved such that:
- thread A gets 
{} - thread B gets 
{} - thread B computes 
{}+fooB -> barB={(fooB -> barB)} - thread B sets 
myMapto{(fooB -> barB)} - thread A computes 
{}+fooA -> barA={(fooA -> barA)} - thread A sets 
myMapto{(fooA -> barA)} - thread B's update is lost
 
Using AtomicReference, I came up with the following, more or less based on the ConcurrentStack methods in the “Nonblocking 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));
}
Is this correct? And if so, is it as good an implementation as I'm likely to get, or is there something simpler (e.g. some Java 8 AtomicReference API I'm missing that implements this pattern)?
回答1:
Using AtomicReference is good in this case. You can use the shortcut method 
public void setBar(Foo foo, Bar bar) {
    myMap.updateAndGet(map -> map.put(foo, bar)));
}
instead. See the javadoc for AtomicReference.updateAndGet. The default java implementation is exactly the same as yours in Java 8.
来源:https://stackoverflow.com/questions/44147719/lock-free-atomic-update-to-immutable-map