微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ConcurrentHashMap.keySet().stream() 的一致性行为

如何解决ConcurrentHashMap.keySet().stream() 的一致性行为

我可以从带有 modifyConcurrentHashMap 方法中获得什么一致性行为?

// map is filled concurrently from multiple threads
private final ConcurrentHashMap<String,Object> map = new ConcurrentHashMap<>();


public void modify(Object newValue) {
  map.keySet().stream()
    .filter(/* some filter */)
    .forEach(k -> {
      if (/* k has some property */) {
        map.put(k,newValue);
      } else {
        map.remove(k);
      }
    });
}

我发现很难通过阅读 Javadocs、keySet()stream() 和流中地图条目的修改如何相互作用来获得明确的答案。我知道 keySet()spliteratorweakly consistent,所以流应该至少遍历所有元素,因为它们在过去的某个时间点只存在一次。并发集合支持在流执行期间修改数据源(参见 Non-interference)。我总是只修改当前键而不涉及 foreach lambda 中的其他映射条目,这可能是相关的。

那么上面的代码是否以弱一致的方式“都很好”,还是有一些我应该注意的警告?


注意:上面的代码也没有真正的意义,但这是省略了其他非并发相关方面的简化代码

使用不同的方法可以更好地编写代码,例如forEach 在地图上。如果它增加了答案,请随时提出改进建议,但请仅提供回答我实际问题的答案。我不是在问如何使它变得更好,我只是想知道我的示例中的所有部分如何相互作用。

解决方法

流至少应该遍历所有元素,因为它们在过去的某个时间点出现过,只有一次

我会从短语中删除“all”,因为这种措辞可能给人一种错误的印象,即流会迭代整个 ConcurrentHashMap 的某个原子快照。
实际上,流可能反映了对地图的(某些)修改,这是在创建流之后发生的。因此,流发出的第一个和最后一个键可能从未同时出现在映射中。

wording for weakly consistent(由 the keySet()'s javadoc 引用)提到:

他们[即Iterators and Spliterators] 保证遍历元素,因为它们在构造时就存在,并且可能(但不保证)反映构造后的任何修改。

Traverser 中还用另一个词 in the javadoc for ConcurrentHashMap(迭代器和拆分器的基类)提到:

封装了containsValue等方法的遍历;也用作其他迭代器和拆分器的基类。

方法提前访问每个在迭代器构建时可到达的仍然有效的节点。它可能会遗漏一些在访问 bin 后添加到 bin 中的内容,这是可以保证一致性的。

“过去的某个时间点”也很模糊:您看不到在创建 stream() 之前从地图中删除的键。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。