【问题标题】:Lock-free atomic update to immutable Map对不可变 Map 的无锁原子更新
【发布时间】:2017-05-24 01:57:37
【问题描述】:

给定一个 Javaslang / Vavr immutable map,以及一个更新该映射的函数:

private Map<Foo, Bar> myMap = HashMap.empty();

public void setBar(Foo foo, Bar bar) {
  myMap = myMap.put(foo, bar);
}

如何确保针对不同的 Foo 密钥对 setBar() 的两个并发调用都会记录其更新?

// thread A
setBar(fooA, barA)

// thread B
setBar(fooB, barB)

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

  1. 线程A得到{}
  2. 线程 B 得到{}
  3. 线程 B 计算 {} + fooB -&gt; barB = {(fooB -&gt; barB)}
  4. 线程 B 将 myMap 设置为 {(fooB -&gt; barB)}
  5. 线程 A 计算 {} + fooA -&gt; barA = {(fooA -&gt; barA)}
  6. 线程 A 将 myMap 设置为 {(fooA -&gt; barA)}
  7. 线程 B 的更新丢失

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

【问题讨论】:

    标签: java immutability atomic vavr


    【解决方案1】:

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

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

    相反。请参阅javadoc for AtomicReference.updateAndGet。默认的 java 实现与您在 Java 8 中的实现完全相同。

    【讨论】:

      猜你喜欢
      • 2012-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多