两个迭代器抛出ConcurrentModificationException

时间:2017-10-08 16:29:59

标签: java iterator concurrentmodification

我有以下代码

public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        Arrays.stream("hello how are you".split(" ")).forEach(s -> list.add(s));

        Iterator<String> it = list.iterator();
        ListIterator<String> lit = list.listIterator();

        while (it.hasNext()) {
            String s = it.next();
            if (s.startsWith("a")) {
                it.remove();
            } else {
                System.out.println(s);
            }
        }

        System.out.println(list);

        // {here}

        while (lit.hasNext()) {
            String s = lit.next();
            if (s.startsWith("a")) {
                lit.set("1111" + s);
            } else {
                System.out.println(s);
            }
        }

        System.out.println(list);
}

在这里,在遍历Iterator之后,我尝试迭代ListIterator。但代码会抛出ConcurrentModificationException。我只在ListIterator完成后才使用Iterator进行修改,但为什么会出现此异常。

当我在{here}而不是顶部初始化ListIterator时,代码运行完美。

  1. 当列表被两个线程同时修改时,是否会抛出ConcurrentModificationException
  2. 初始化迭代器,在列表上创建一个锁吗?如果是,那么为什么Java允许我们在Iterator已经被另一个Iterator初始化之后初始化它?

4 个答案:

答案 0 :(得分:2)

  

当两个线程同时修改列表时,是否抛出了ConcurrentModificationException?

不一定。 ConcurrentModificationException表示在创建Iterator后,列表已在结构上发生了变化(remove自己的Iterator方法除外)。这可能是由于多个线程使用相同的列表,或者可能是由于尝试在不使用ArrayList的情况下从每个循环中的Iterator内删除项目。

  

初始化迭代器,在列表上创建一个锁吗?

不,没有锁。创建Iterator时,它会记录modCount的{​​{1}}(列表状态的粗略表示,在每次结构更改时递增)。如果迭代器检测到对ArrayList的modcount的更改,该modcount不是由自己的方法引起的,则抛出异常。

您正在从第二个迭代器获取异常,因为在实例化和使用的第二个迭代器之间对列表进行了结构更改。

  

为什么Java允许我们在迭代器已经被另一个迭代器初始化后初始化它?

List不会跟踪它创建的所有迭代器或它们的状态。这样做会使实施大大复杂化。 ArrayList方法并不完美,有点粗糙,但它很简单并且识别出许多真正的错误。

答案 1 :(得分:1)

使用第一个迭代器后,必须加载第二个迭代器。否则第二个迭代器“认为”列表没有被改变,但实际上它确实如此。因为列表被更改了,所以第二个迭代器的反应就像“等一下,不应该在这里/已经消失”并抛出一个ConcurrentModificationException

它允许您随时初始化迭代器。如果你不改变内容,你可能会很好,并且没有得到ConcurrentModificationException,因为没有任何改变。

答案 2 :(得分:1)

每当您尝试使用无效迭代器时,都可能抛出ConcurrentModificationException - 无论何时创建迭代器,然后从不同的访问点修改基础集合,都会发生这种情况。在这里,初始化lit,然后通过it修改列表,使其无效,这解释了异常。

答案 3 :(得分:0)

ListIterator抛出ConcurrentModificationException,它在创建后在列表中有一个修改。在您的代码中,您同时创建了Iterator和ListIterator,稍后您将从列表中删除导致ConcurrentModificationException的内容。

要避免这种情况,请将代码更改为低于1。您只需要在迭代器操作后移动ListIterator初始化。

try (PreparedStatement ps = conn.prepareStatement("insert into a(b, c) values (?, ?)") ) {
    while(var4.next()) {
        ps.setInt(1, rs.getInt("b"));
        ps.setInt(2, rs.getInt("c"));
        ps.executeUpdate();
    }
} catch( SQLException e){
    ...
}