ArrayList.addAll()引发ConcurrentModificationException异常

4

在实现Java 8(OpenJDK)中的Kademlia存储桶时,我遇到了一些非常奇怪的问题。

我需要从所谓的存储桶中获取至少特定数量的项。但这不是问题所在。

不知何故,有时候在ArrayList上执行closest.addAll()时会出现ConcurrentModificationException,尽管它只在单个线程中使用,而且我没有迭代或做类似的事情。

您知道如何帮助我吗?这是我的代码(我知道它很混乱!):

List<Neighbour> getClosest(Node n, int num) {
    ArrayList<Neighbour> closest = new ArrayList<>();
    int missing;
    int walkDown = n.getBucket(me);
    int walkUp = walkDown + 1;
    boolean pleaseBreak = true;
    while (true) {
        missing = num - closest.size();
        if (missing <= 0) {
            return closest;
        }

        if (walkUp >= 0 && walkUp < 160) {
            List<Neighbour> l = buckets[walkUp].getClosest(missing);
            closest.addAll(l);
            if (closest.size() >= missing) {
                return closest;
            }
            walkUp++;
            pleaseBreak = false;
        }

        if (walkDown >= 0 && walkDown < 160) {
            List<Neighbour> l = buckets[walkDown].getClosest(missing);
            closest.addAll(l);
            if (closest.size() >= missing) {
                return closest;
            }
            walkDown--;
            pleaseBreak = false;
        }

        if (pleaseBreak) {
            return closest;
        }
        pleaseBreak = true;
    }
}

buckets[walkDown] 是什么类型?你能展示一下 getClosest(missing) 的代码吗? - Eran
请提供一个 mcve,这看起来很好。 - BeyelerStudios
请提供一个堆栈跟踪。 - Natix
1个回答

4

ConcurrentModificationException 实际上意味着您在迭代列表时以某种方式修改了该列表,从而违反了迭代规则。

请注意,此异常并不总是表示对象已被不同线程同时修改。如果单个线程发出违反对象合同的方法调用序列,则对象可能会抛出此异常。例如,如果线程在使用快速失败的迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。

也就是说,很清楚可能导致此问题的原因。由于 closest 是被该方法填充的新 List,因此必须是 l 被修改了。

有两个选项:

  1. 另一个线程正在执行此操作。
  2. 您已经打开了跨列表 l 的迭代器。

假设不是情况 1(否则您可能已经提到了),我会选择:

你的 getClosest 方法返回了一个正在被迭代和/或修改的列表的子列表,而 addAll 也试图对其进行迭代。
要修复这个问题,需要让 getClosest 返回子列表的副本

谢谢,我真是个傻瓜。现在它运行得很好,我只是使用了一个复制构造函数。 - Marcel
@Marcel - 这是一个常见的错误。复制构造函数是一个好的解决方案。 - OldCurmudgeon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接