【问题标题】:ConcurrentHashMap read/update only via compute methodConcurrentHashMap 仅通过计算方法读取/更新
【发布时间】:2021-06-03 16:57:42
【问题描述】:

这基本上是thisthis 或许多其他在这方面的延续。

我的问题可能很简单,如果我只使用ConcurrentHashMap::compute,在读者和作者中都有一定的价值,这是否足以确保可见性

我知道compute 方法:

整个方法调用都是原子执行的

这足以保证可见性吗?具体来说,关于happens-before,真正的规范/文档是否明智?为了简化我的问题,这里有一个例子:

static class State {
    private int age;

    int getAge() {
        return age;
    }

    State setAge(int age) {
        this.age = age;
        return this;
    }
} 

和:

// very simplified, no checks
static class Holder {

    private static final ConcurrentHashMap<String, State> CHM = new ConcurrentHashMap<>();

    public State read(String key) {
        return CHM.compute(key, (x, y) -> y);
    }

    public State write(String key, int age) {
        return CHM.compute(key, (x, y) -> y.setAge(y.getAge() + age));
    }
}

没有人可以访问CHM,只能通过Holder工作。

对我来说答案显然是肯定的,这是安全的,所有读者都会看到最新的write 方法的结果。我只是无法将这些点与ConcurrentHashMap 的文档联系起来,这很可能是显而易见的,但我似乎错过了。

【问题讨论】:

  • 我认为这是线程安全保证所暗示的。
  • 你已经回到了这个问题explained here。问题是,read 的调用者是做什么的?它将看到“最新write 的结果”,但哪个写入是最新写入?如果没有任何定义的顺序,可能还没有发生任何事情,并且所有这些都可能正在运行,同时与read 方法的调用者同时运行。
  • 是的,它的可见性已经足够了。我认为这对于能见度来说有点太多了。对于read() 方法,您可以使用CHM.get() 并从此过上幸福的生活。
  • @TamasRev 不,你不能使用get。我链接的 cmets 和最初的问题讨论的范围更广。
  • @Holger 你介意发布答案吗?我终于明白你的意思了。

标签: java multithreading concurrenthashmap happens-before


【解决方案1】:

compute()javadoc says 这个方法的作用:

尝试计算指定键及其当前映射值的映射(如果没有当前映射,则为 null)。

所以compute() 替换键的值。

使用compute() 来修改某些对象的内部字段(即使该对象作为值存储在映射中)并不是compute() 的本意。
因此,compute() 的规范/文档自然而然地保证(甚至说)对此一无所知。

关于happens-before,文档中有多次提及:

  • ConcurrentHashMap:

    更正式地说,给定键的更新操作与报告更新值的键的任何(非空)检索具有 happens-before 关系。

  • ConcurrentMap:

    内存一致性效果:与其他并发集合一样,线程中的操作在将对象放入 ConcurrentMap 作为键或值之前 happen-before 在访问或删除对象之后的操作该对象来自另一个线程中的ConcurrentMap

  • java.util.concurrent:

    在将对象放入任何并发集合之前线程中的操作happen-before 在另一个线程中从集合中访问或删除该元素之后的操作。

重要的是,happen-before 关系仅在将对象插入/删除/检索到集合之间得到保证。
在您的情况下,它是相同的 State 对象(仅在内部更新其字段),因此根据文档 ConcurrentHashMap 甚至允许 IMO 确定没有任何更改并跳过剩余的同步步骤。

【讨论】:

  • 我不知道您在第一段中的哪个位置决定 replace 值并且不允许突变,如果您问,这仍然是替换我。然后简单地突出显示 happens-before 并不是我所追求的,到目前为止。我不认为这回答了我的问题。过段时间再读一遍我可能会修改我的意见,但目前我不能接受。
猜你喜欢
  • 2018-05-24
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
  • 1970-01-01
  • 2018-04-06
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多