Lock-free atomic update to immutable Map

巧了我就是萌 提交于 2020-06-25 05:18:26

问题


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:

  1. thread A gets {}
  2. thread B gets {}
  3. thread B computes {} + fooB -> barB = {(fooB -> barB)}
  4. thread B sets myMap to {(fooB -> barB)}
  5. thread A computes {} + fooA -> barA = {(fooA -> barA)}
  6. thread A sets myMap to {(fooA -> barA)}
  7. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!