【问题标题】:Java List and recursion leads to Concurrent Modification ExceptionJava List 和递归导致并发修改异常
【发布时间】:2012-02-03 09:47:29
【问题描述】:

下面的函数递归遍历一个列表,并将其除以一半,并对子列表做一些事情。当 listsize 为 2 时,递归中断。我知道如果在迭代列表时更改列表,则会发生并发修改异常。但我不使用迭代,它仍然会发生:

    private static List<ParticipantSlot> divide(List<ParticipantSlot> list) {
        int n = list.size();

        //do something 

        if (n>2){
            List<ParticipantSlot> l = divide(list.subList(0, n/2-1));
            List<ParticipantSlot> r= divide(list.subList(n/2, n));

            l.addAll(r);
            return l;
        }else{
            return list;
        }
    }

【问题讨论】:

  • subList 不会复制列表,如果为您提供完整列表支持的列表。

标签: java list recursion concurrentmodification


【解决方案1】:

如果您使用的是 ArrayList,您可能需要将其更改为 CopyOnWriteArrayListConcurrentLinkedQueue

如果您在多线程环境中,则需要在数组周围放置一个synchronized

希望对你有帮助。

【讨论】:

    【解决方案2】:

    您正在使用addAll(),它将遍历您在参数中提供的集合。现在subList 仅将 view 返回到原始列表中,因此您尝试将值添加到原始列表的视图中,并同时迭代原始列表的不同部分.砰。

    如果您每次都创建子列表的副本,它应该可以工作 - 尽管效率会很低。

    【讨论】:

    • 谢谢,终于解释清楚了。
    【解决方案3】:

    您会得到一个并发修改异常,因为子列表由原始列表支持:

    返回的列表是由这个列表支持的,所以返回列表中的非结构性变化会反映在这个列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。

    如果您想避免异常,请在修改前复制第一个子列表。

    【讨论】:

    • 仍然,并发修改异常究竟是如何抛出的?
    • @MikeNakis 对l.addAll(r) 的调用通过调用sublist 获得的“视图”间接修改了list
    • 如果我有一个金发碧眼的时刻,请原谅,但是...同时将其修改为什么?我没有看到列表上同时发生任何迭代或其他任何事情。或者你是说按照设计,如果一个子列表已经从列表中创建并且还没有 GCd,那么对列表的任何修改都必然会导致“并发修改”异常?这有点奇怪,因为这意味着一旦我获取了一个子列表,我就永远无法修改原始列表,因为我无法保证子列表何时或是否会被 GCd。
    • @MikeNakis “同时将其修改为什么?”它同时修改列表以通过子列表访问其中的一部分。通常,这会使子列表无效,因为数据在窗口中“移动”、进入窗口或从窗口中消失。只有通过它读取原始列表的子列表才重要:在修改“基本”列表后保留子列表是可以的,即使该子列表没有资格进行 GC,只要您不再通过它访问基本列表。在读取时检测到并发修改错误,而不是在写入时检测到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2017-07-14
    • 1970-01-01
    • 2013-03-01
    • 1970-01-01
    相关资源
    最近更新 更多