45

我有一小段代码,它给了我并发修改异常。我不明白为什么我一直得到它,即使我没有看到正在执行任何并发修改。

import java.util.*;

public class SomeClass {
    public static void main(String[] args) {
        List<String> s = new ArrayList<>();
        ListIterator<String> it = s.listIterator();

        for (String a : args)
            s.add(a);

        if (it.hasNext())
            String item = it.next();

        System.out.println(s);
    }
}
4

9 回答 9

54

为避免ConcurrentModificationException,您应该像这样编写代码:

import java.util.*;

public class SomeClass {

    public static void main(String[] args) {
        List<String> s = new ArrayList<String>();

        for(String a : args)
            s.add(a);

        ListIterator<String> it = s.listIterator();    
        if(it.hasNext()) {  
            String item = it.next();   
        }  

        System.out.println(s);

    }
}

Ajava.util.ListIterator允许您在迭代期间修改列表,但不能在创建和使用之间修改。

于 2009-09-30T05:14:57.030 回答
53

我不明白为什么我一直得到它,即使我没有看到正在执行任何并发修改。

在创建迭代器和开始使用迭代器之间,您向要迭代的列表添加了参数。这是一个并发修改。

    ListIterator<String> it = s.listIterator();  

    for (String a : args)
        s.add(a);                    // concurrent modification here

    if (it.hasNext())
        String item = it.next();     // exception thrown here

完成向列表中添加元素后创建迭代器:

    for (String a : args)
        s.add(a); 

    ListIterator<String> it = s.listIterator();  
    if (it.hasNext())
        String item = it.next();
于 2009-09-30T05:09:01.723 回答
12

来自JavaDoc:对于 ConcurrentModificatoinException:“通常不允许一个线程在另一个线程对其进行迭代时修改 Collection”。

它只是意味着如果您仍然有一个打开的迭代器,则不允许您修改列表,因为迭代器循环将中断。尝试移动ListIterator<String> it = s.listIterator();到 for 循环之后。

于 2009-09-30T05:07:59.400 回答
8

修改基础列表后,不允许您继续迭代迭代器。在这里,您在向 中添加一些项目之前创建迭代器,然后在添加s之后继续对其执行 ahasNext()和 a next(),导致ConcurrentModificationException

于 2009-09-30T05:07:11.177 回答
5

如果上述解决方案不能正常工作。您可以在添加新项目的同时使用旧的 for 循环来迭代 List。请参见下面的示例:

import java.util.*;

public class SomeClass {
    public static void main(String[] args) {
        ArrayList<AClass> aList = new ArrayList<AClass>(); // we will iterate this


        // this will cause ConcurrentModificationException. 
        // Since we are iterating the list, at the same time modifying it.
        /*for(AClass a: aList){
           aList.add(someMethod(a));
        }*/

        // old fashion for-loop will help
        int limit = aList.size();
        for(int i=0; ctr<limit; ++i){
           AClass a = aList.get(i);
           aList.add(someMethod(a));
        }


    }
}
于 2013-12-11T11:05:40.143 回答
3

要理解这一点,让我们看看 HashMap 实现的来源:

public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable{

其中包含 HashIterator 如下:

private abstract class HashIterator {
    ...
    int expectedModCount = modCount;
    ...

    HashMapEntry<K, V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        .... 
        }

每次创建迭代器时:

  • 创建了一个计数器expectedModCount并将其设置为 modCount的值作为入口检查点
  • modCount 在使用 put/get (add/remove) 的情况下增加
  • 迭代器的nextEntry方法正在用当前的 modCount 检查这个值,如果它们不同,则抛出并发修改异常

为避免这种情况,您可以:

  • 将地图转换为数组(不推荐用于大型地图)
  • 使用并发映射或列表类(CopyOnWriteArrayList / ConcurrentMap
  • 锁定映射(这种方法消除了多线程的好处)

这将允许您同时迭代和添加或删除元素而不会引发异常

并发映射/列表迭代器是一个“弱一致”迭代器,它永远不会抛出 ConcurrentModificationException,并保证在构造迭代器时遍历元素,并且可能(但不保证)反映构造后的任何修改。

有关 CopyOnWriteArrayList 的更多信息

于 2015-12-25T23:54:23.597 回答
3

ConcurrentModificationException可能出现在单线程环境和多线程环境中。主要问题是所有通用迭代器(如 ArrayList 中使用的迭代器)都是FailFast 迭代器,当我们尝试修改一个列表时,如果一个迭代器已经在迭代它就会失败。解决方案 - > 如果需求需要这种情况,则使用 CopyOnWriteArrayList 而不是使用 ArrayList。

对于一个完整的演示,可以使用下面提到的代码。我们只需要将实现从 CopyOnWriteArrayList 更改为 ArrayList。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author narif
 *
 */
public class TestApp {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List<String> testList = new ArrayList<>();
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add(6, "abcAtindex6");
        int size = testList.size();
        System.out.println("The Current List (ArrayList) is: " + testList);
        System.out.println("The size of the List (ArrayList) is: " + size);
        /* Comment the below lines to get the ConcurrentModificationException */
        testList = new CopyOnWriteArrayList<>(testList);
        for (String value : testList) {
            System.out.println("The Value from ForEach Loop is: " + value);
            /*
             * Concurrent modification is happening here
             * One iterator is iterating over the list while we are trying to add new values to
             * the list so the results of the iteration are undefined under these circumstances.
             * So teh fail fast iterators will fail and will throw the ConcurrentModificationException.
             */
            testList.add("valueFromForLoop");
            testList.add("anotherValueFromForEachLoop");
        }
        Iterator<String> it = testList.iterator();
        while (it.hasNext()) {
            String abc = it.next();
            System.out.println(abc);
            testList.add("Value from Iterator1");
            testList.add("Value from Iterator2");
            testList.add("Value from Iterator3");
            testList.add("Value from Iterator4");

        }
        System.out.println("Did the modificationa and all after conevrting the ArrayList to CopyOnWriteArrayList.");
        System.out.println("Calling the method to get the new List..");
        testList = new CopyOnWriteArrayList<>(getTheList(testList));
        for (String value : testList) {
            System.out.println("The value returned from method is : " + value);
        }
    }

    private static List<String> getTheList(List<String> pList) {
        List<String> list = new CopyOnWriteArrayList<>(pList);
        int i = 0;
        for (String lValue : list) {
            System.out.println("The list Passed is " + list);
            i++;
            list.add("localVaueFromMethod" + i);
            list.removeAll(pList);
        }
        return list;
    }

}

有关更多信息,请点击此链接,这可能会很有帮助ConcurrentModificationException Java Docs

于 2016-03-31T10:13:20.080 回答
1

这没有用:

LinkedList<String> linkedList = new LinkedList<String>();
ListIterator listIterator = linkedList.listIterator();
linkedList.add("aa");
linkedList.add("bb");

这有效:

LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("aa");
linkedList.add("bb");
ListIterator listIterator = linkedList.listIterator();
于 2014-09-03T20:49:00.077 回答
1

查看 oracle文档页面。

public class ConcurrentModificationException
extends RuntimeException

当这种修改是不允许的时,检测到对象的并发修改的方法可能会抛出此异常

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

在您的情况下,您在创建迭代器后修改了集合,因此您遇到了异常。

如果您按照Stephen C的回答更改代码,则不会出现此错误。

于 2016-03-02T00:49:18.300 回答