【问题标题】:Adding and removing values from ConcurrentHashMap while iterating over it在迭代时从 ConcurrentHashMap 添加和删除值
【发布时间】:2017-01-03 18:08:06
【问题描述】:

我有这个代码:

private ConcurrentMap<String, Integer> myMap = new ConcurrentHashMap<>();

 @Scheduled(fixedDelay = 600_000)
 public void foo(){     
    myMap.values().stream().
                filter(predicate()).
                forEach(this::remove);
}

public void insert(String str, Integer value){
   myMap.put(str, value);
}

如果在迭代此映射时会发生什么 - 有人将新值放入其中或从中删除现有值?

【问题讨论】:

  • 你有什么尝试吗?
  • @BuhakeSindi - 不确定我明白你在问什么
  • 我的意思是您是否创建了一个与您的场景相匹配的测试用例。
  • @BuhakeSindi - 哦...我不知道如何编写这样的测试
  • 不清楚this::remove 应该做什么。这意味着 your 类中必须有一个remove 方法接收一个值来执行what?再次遍历地图,以删除该值?请考虑使用myMap.values().removeIf(predicate());...

标签: java collections concurrency java-8 java-stream


【解决方案1】:

ConcurrentHashMap 的文档包含有关该行为的一些详细信息。首先我们看看ConcurrentHashMap.values()做了什么:

返回此映射中包含的值的Collection 视图...

视图的迭代器和拆分器弱一致

视图的spliterator 报告Spliterator.CONCURRENTSpliterator.NONNULL

有趣的是"weakly consistent"Spliterator.CONCURRENT这两个术语,其中前者被描述为:

大多数并发 Collection 实现(包括大多数队列)也不同于通常的 java.util 约定,因为它们的 Iterators 和 Spliterators 提供弱一致而不是快速失败遍历:

  • 它们可能与其他操作同时进行
  • 他们永远不会扔ConcurrentModificationException
  • 保证它们只遍历构建时存在的元素一次,并且可能(但不保证)反映构建后的任何修改。

Spliterator.CONCURRENT被描述为:

表示元素源可以被多个线程安全地同时修改(允许添加、替换和/或删除)的特征值,而无需外部同步。如果是这样,Spliterator 应该有一个关于遍历期间修改的影响的文档化策略。

根据所有这些文档,并且与ConcurrentHashMap 的并发模型一致,这意味着流管道是完全线程安全的,并且将遍历创建迭代器时存在的元素。

【讨论】:

  • 谢谢!在@BuhakeSindi 的问题之后,我尝试编写一个单元测试,该测试使用多个线程来添加和删除该映射中的元素(一些用于插入,一些用于删除)-在那段时间里,我运行了我的 foo() 方法确实 - 没有造成任何伤害......
猜你喜欢
  • 1970-01-01
  • 2016-11-23
  • 2016-01-02
  • 2012-06-28
  • 2014-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多