如何解决ConcurrentHashMap.keySet().stream() 的一致性行为
我可以从带有 modify
的 ConcurrentHashMap
方法中获得什么一致性行为?
// 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()
的 spliterator
是 weakly 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 举报,一经查实,本站将立刻删除。