【问题标题】:Iterating and removing elements from ConcurrentLinkedQueue从 ConcurrentLinkedQueue 中迭代和删除元素
【发布时间】:2021-03-14 09:01:24
【问题描述】:

我已经定义了一个 ConcurrentLinkedQueue 并像这样循环它:

 ConcurrentLinkedQueue clq 
 .forEach((carTask -> {
        

现在,如果我从队列中删除 carTask。这是一个元素
在我们执行 forEach 时在队列中会导致任何问题吗?

我想到的另一种选择是放置要删除的元素
在一个单独的列表中,一旦 forEach 结束,只需删除所有
列表中队列中的元素。
我不确定这种替代方法是否能完美运行
有什么建议或更好的方法来解决这个问题?

【问题讨论】:

    标签: java queue java.util.concurrent


    【解决方案1】:

    这个具体的例子不会导致错误,因为Queue的这个实现允许并发修改。

    但是通过remove 方法从Java 集合中删除元素,当您循环遍历元素时,通常会导致ConcurrentModificationException。相反,使用Iterator 并调用其remove 方法是一个好习惯:

    Collection<String> c = new LinkedList<>();
    c.add("foo");
    c.add("bar");
    for(Iterator<String> it = c.iterator(); it.hasNext(); ) {
      String s = it.next();
      if(s.equals("foo")) {
        it.remove();
      }
    }
    // collection now contains only "bar"
    

    除了防止ConcurrentModificationException,这是更可取的,因为remove 对于许多集合的时间复杂度是线性。由于该队列是作为链表实现的,因此必须遍历该列表,直到找到要删除的元素。使用Iterator,您已经找到了该元素,并且可以“就地”删除它。

    如果您找到要删除的元素,并且它是队列中的最后一个元素,该怎么办?在该项目上调用remove 需要一直遍历到队列的末尾再次。此外,它需要为每个前面的元素调用equals。您绝对不想将要删除的项目放入列表中,因为您将具有相同的负面性能特征(以及分配另一个 List)。

    【讨论】:

    • 非常好的,深入的答案。
    • 所以即使一个元素被移除。迭代器跟踪它并且 for 循环运行良好。如果在迭代器期间另一个线程将一个元素添加到队列中会被迭代怎么办?或者只有在迭代器开头的元素才会被迭代
    • 这真的取决于您使用的集合!您应该始终阅读迭代器方法的 Javadoc 以检查其保证。对于ConcurrentLinkedQueue,迭代器弱一致docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/…
    猜你喜欢
    • 2013-03-23
    • 1970-01-01
    • 2015-06-30
    • 2012-05-13
    • 2011-02-21
    相关资源
    最近更新 更多