【问题标题】:Java Remove Specific Item From ConcurrentHashMapJava 从 ConcurrentHashMap 中删除特定项目
【发布时间】:2015-01-09 19:41:51
【问题描述】:

使用 remove() 方法好吗?我读过一篇文章说同步没有被添加到remove方法中。如何从 ConcurrentHashMap 中正确删除 特定 项?

示例代码:

    ConcurrentHashMap<String,Integer> storage = new ConcurrentHashMap<String,Integer>();
    storage.put("First", 1);
    storage.put("Second", 2);
    storage.put("Third",3);


    //Is this the proper way of removing a specific item from a tread-safe collection?
    storage.remove("First");

    for (Entry<String, Integer> entry : storage.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        // ...
        System.out.println(key + " " + value);
    }

【问题讨论】:

  • “我读过一篇文章说 remove 方法中没有添加同步” 那篇文章写的是 Java 5、6、7 还是 8 的时代?也许它是考虑到较旧的 Java 版本编写的。
  • 希望此链接有效javarevisited.blogspot.ca/2013/02/…。他说“由于 put()、remove()、putAll() 或 clear() 等更新操作未同步阅读更多:javarevisited.blogspot.com/2013/02/…
  • @peter.petrov - 像我在代码示例中所做的那样在 ConcurrentHashMap 上调用 remove 方法是可以的,对吗?线程安全应该不是问题?
  • 不完全确定。我猜这篇文章的意思如下:在remove 中,并非整个表都被锁定,而只是锁定了其中的一部分(如 manouti 发布的代码所示)。至少这就是我阅读这篇文章及其含义的方式。所以这一切都取决于你的并发场景。不确定,如果我要确定地回答,需要自己从头开始研究。
  • @peter.petrov - 嗯,那我如何从 concurrecthashmap 中删除特定项目?

标签: java thread-safety java.util.concurrent concurrenthashmap


【解决方案1】:

Iterator 应该可以完成这项工作:

Iterator<Map.Entry<String, Integer>> iterator = storage.entrySet().iterator();
while(iterator.hasNext())
{
    Map.Entry<String, Integer> entry = iterator.next();
    if(entry.getKey().equals("First"))
    {
       iterator.remove();
    }
 }

参考:https://dzone.com/articles/removing-entries-hashmap

【讨论】:

    【解决方案2】:

    remove 方法确实在锁上同步。确实检查ConcurrentHashMap#remove()的代码,有一个获取锁的lock方法的调用:

    public V remove(Object key) {
        int hash = hash(key.hashCode());
        return segmentFor(hash).remove(key, hash, null);
    }
    

    其中ConcurrentHashMap.Segment#remove(key, hash, null) 定义为:

    V remove(Object key, int hash, Object value) {
         lock();
         try {
            ...
    

    注意Javadoc的描述:

    检索操作(包括get)一般不会阻塞,因此可能与更新操作(包括putremove)重叠。检索反映了最近完成更新操作在其开始时保持的结果。对于putAllclear 等聚合操作,并发检索可能仅反映插入或删除某些条目。类似地,迭代器和枚举返回反映哈希表在创建迭代器/枚举时或之后的某个时间点的状态的元素。他们确实抛出ConcurrentModificationException。但是,迭代器被设计为一次只能由一个线程使用。

    【讨论】:

    • 我在代码示例中使用的删除方法还可以,对吧?我不必担心线程安全?
    • 是的,没关系。它是线程安全的,因为使用它不需要锁定,也不会发生ConcurrentModificationException
    • 再次:作者只是说 remove 不是在整个表对象上同步的。至少我是这么看的。
    • @peter.petrov - 只是为了确认,我会放弃这种情况,我在上面的示例代码中调用它的方式很好,我不必担心线程 -安全吗?
    • @spudone 是的,这并不矛盾。它们是线程安全的,这是这里正在讨论的一致性级别(请参阅发布的 javadoc mahouti)。所以例如我猜在一些复杂的场景中,get 操作可能会看到表中已经存在removed 的内容:) 这就是我阅读所有这些的方式。考虑一个具有 RDBMS 和隔离级别的类比。希望你明白我的意思。
    【解决方案3】:

    你可以直接在 entrySet 上使用 removeIf :

    map.entrySet().removeIf( entry -> .. some condicion on entry ) 
    

    请注意Java 8 中存在一个错误,该错误仅在 Java 9 中修复 (here)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-21
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多