无锁原子更新到不可变Map

时间:2017-05-24 01:57:38

标签: java immutability atomicity vavr

给定Javaslang / Vavr immutable map,以及更新该地图的函数:

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)

似乎存在调用交错的风险:

  1. 主题A得到{}
  2. 主题B得到{}
  3. 主题B计算{} + fooB -> barB = {(fooB -> barB)}
  4. 主题B将myMap设置为{(fooB -> barB)}
  5. 主题A计算{} + fooA -> barA = {(fooA -> barA)}
  6. 主题A将myMap设置为{(fooA -> barA)}
  7. 线程B的更新丢失
  8. 使用AtomicReference,我或多或少地根据Java Concurrency in Practice的“非阻止算法”部分中的ConcurrentStack方法提出了以下内容。

    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 AtomicReference API)?

1 个答案:

答案 0 :(得分:1)

在这种情况下使用AtomicReference是好的。您可以使用快捷方式

public void setBar(Foo foo, Bar bar) {
    myMap.updateAndGet(map -> map.put(foo, bar)));
}

代替。请参阅javadoc for AtomicReference.updateAndGet。默认的Java实现与Java 8中的实现完全相同。