繁体   English   中英

列表抛出 ConcurrentModificationException 传递给 CompletableFutures

[英]List throwing ConcurrentModificationException passed to CompletableFutures

场景 1:::
所以我有params列表,它被传递给调用 Web 服务并获取数据的 2 个方法。 这些 to 方法只是在params列表上执行stream.filter.collect以获得休息调用所需的参数。 现在我已经使用CompletableFutures进行了 2 个并行调用。 这可以抛出ConcurrentModifcation异常吗?

场景 2:::
与上面类似的设置,只是现在一种方法更改参数列表并向其中添加一些对象。 我知道这是抛出Concurrent Modification exp。 我应该将列表作为copyonWriteArraylist还是创建带有深复制的新列表以避免任何进一步的问题。

场景#1:可能不是,但您的描述过于模糊,无法确定。

场景#2:绝对是。

发生 CoModEx 唯一需要的是列表以任何方式更改。 无论是addaddAllclearremoveretainAll还是List上具有更改列表本身效果的任何其他方法。 甚至获取子列表并更改那个(因为从创建子列表的“外部”列表中可以看到对子列表的更改)。

CoModEx,尽管使用了“并发”这个词,但与线程有关。 事实上,同时处理来自两个线程的列表是少数几种可以破坏事物的方法之一(方法不再按照其 javadoc 所说的去做)而不会导致 ConcurrentModificationException(将取决于竞争条件如何进行)。

这是获取 CoModEx 的一种简单方法:

var list = new ArrayList<String>();
list.add("Hello");
list.add("World");
for (String item : list) if (item.equals("Hello")) list.remove(item);

那会扔掉它。 每次。 CoModEx 由迭代器抛出(并且for (x:y)构造函数将隐式创建迭代器,就像x.stream()... ,它创建一个拆分器,它也这样做)当底层数据结构在任何不是由(spl)迭代器本身直接完成的方式。 例如,这是使用不会导致 CoModEx 的迭代器从您自己的列表中删除内容的一种方法:

var it = list.iterator();
while (it.hasNext()) {
    if (it.next().startsWith("Hello")) it.remove();
}

注意我正在调用迭代器的删除,而不是列表的删除,这会导致 CoModEx:这会改变底层列表(而不是直接通过迭代器),因此在修改之前创建的迭代器上的任何操作都会抛出 CoModEx。

所以,这是流程:

  1. 通过输入for (String item : list)list创建迭代器。
  2. hasNext()该迭代器的hasNext()以检查是否应进入 for 循环。 它返回true
  3. 该迭代器的next()在第一个循环中被调用; Hello回来了。
  4. 由于 for 循环内的代码, list.remove("Hello")被调用。 这将使迄今为止由该列表创建的所有迭代器“无效”。
  5. for 循环循环,并调用hasNext()来检查它是否应该再次循环。
  6. hasNext 意识到它无效,并抛出 CoModEx。

ArrayList 通过有一个计数器来实现这一点,该计数器在每次发生任何变化时都会增加,并且所有迭代器在创建时都会记住计数器的值,并检查列表的计数器值是否等于它们自己的值。 如果没有,他们会抛出 CoModEx。 如果需要,其他列表实现可以使用不同的机制。 有些人CopyOnWriteArrayList地实际允许这样做(例如CopyOnWriteArrayList ,它明确详细说明了它如何让您在迭代期间修改自身)。

如果涉及多个线程,则所有赌注都将关闭 - 这些计数器写入不同步,因此可能会或可能不会被所涉及的线程看到。 除非您真的知道自己在做什么,否则不要从不同的线程访问相同的列表。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM