gpt4 book ai didi

java - 尝试运行 `ConcurrentModificationException` 方法时 ArrayList 抛出 `.size()`

转载 作者:行者123 更新时间:2023-11-30 06:01:58 28 4
gpt4 key购买 nike

更新

正如 Jiri Tousek 所指出的,我的代码中抛出的错误误导了许多业余(和经验丰富的)Java 开发人员。与名称所暗示的相反,ConcurrentModificationException 与多线程没有有任何关系。考虑以下代码:

import java.util.List;
import java.util.ArrayList;

class Main {
public static void main(String[] args) {
List<String> originalArray = new ArrayList<>();
originalArray.add("foo");
originalArray.add("bar");
List<String> arraySlice = originalArray.subList(0, 1);
originalArray.remove(0);
System.out.println(Integer.toString(arraySlice.size()));
}
}

尽管不涉及线程,这仍会引发 ConcurrentModificationException

误导性的异常名称让我认为我的问题是我处理多线程的方式造成的。我已用实际问题更新了帖子的标题。

原文(标题:如何通知Java您已完成在线程中修改ArrayList?)

我的代码大致如下所示:

class MessageQueue {
private List<String> messages = new ArrayList<>();
private List<String> messagesInFlight = new ArrayList<>();

public void add(String message) {
messages.add(message);
}

public void send() {
if (messagesInFlight.size() > 0) {
// Wait for previous request to finish
return;
}

messagesInFlight = messages.subList(0, Math.min(messages.size, 10));
for( int i = 0; i < messagesInFlight.size(); i++ )
{
messages.remove(0);
}

sendViaHTTP(messagesInFlight, new Callback() {
@Override
public void run() {
messagesInFlight.clear();
}
});
}
}

这在我的代码中用于分析目的。每隔 10 秒,我就会从计时器调用 messageQueue.send(),每当发生感兴趣的事件时,我都会调用 messageQueue.add()。这个类的工作*大部分* - 我可以添加消息,它们通过 HTTP 发送,当 HTTP 请求完成时,回调运行

问题出在计时器的第二个滴答声上。当我点击 if (messagesInFlight.size() > 0) { 行时,出现以下错误:

java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.size(ArrayList.java:1057)

似乎我无法在一个线程(第二个计时器的回调)中读取数组的 .size() 因为它认为该数组仍在被另一个线程(第一个计时器的回调)。但是,我希望第一个计时器的线程在调用 sendViaHTTP 后被销毁并清理,因为没有其他代码可供执行。此外,HTTP 请求在 500 毫秒内完成,因此整整 9.5 秒过去了,没有任何内容触及空的 messagesInFlight 数组

有没有办法说“嘿,我已经修改完这个数组了,人们现在可以安全地读取它了”?或者也许是组织代码的更好方法?

最佳答案

您遇到的最明显的问题是您正在使用 ArrayList.subList(),但您似乎并不了解它的真正用途:

Returns a view of the portion of this list ... The returned list is backed by this list.

您在 messagesInFlight 中存储的内容是 View ,而不是副本。当您从 messages 中删除第一条消息时,实际上是在调用 subList() 后立即删除 messagesInFlight 中的消息。所以在for循环之后,将会出现完全不同的消息,并且前n条消息将完全丢失。


至于为什么您会收到您所看到的错误 - subList() 特别允许对子列表和原始列表进行非结构性更改(非结构性更改)意味着替换元素,而不是添加或删除它们),文档中的示例还展示了如何通过修改子列表来修改原始列表。但是,不允许修改原始列表,然后通过子列表访问它,并且可能会导致 ConcurrentModifcationException,类似于更改使用迭代器迭代的列表时发生的情况。

关于java - 尝试运行 `ConcurrentModificationException` 方法时 ArrayList 抛出 `.size()`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56259981/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com