并发修改异常

发布于 2024-08-06 03:08:48 字数 447 浏览 10 评论 0原文

我有一小段代码,它给了我并发修改异常。我无法理解为什么我不断收到它,即使我没有看到任何并发修改正在进行。

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);
    }
}

I have this little piece of code and it gives me the concurrent modification exception. I cannot understand why I keep getting it, even though I do not see any concurrent modifications being carried out.

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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

情泪▽动烟 2024-08-13 03:08:48

为了避免 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);

    }
}

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

To avoid the ConcurrentModificationException, you should write your code like this:

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);

    }
}

A java.util.ListIterator allows you to modify a list during iteration, but not between creating it and using it.

七秒鱼° 2024-08-13 03:08:48

我无法理解为什么我不断收到它,即使我没有看到任何并发修改正在进行。

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

    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();

I cannot understand why I keep getting it, even though I do not see any concurrent modifications being carried out.

Between creating the iterator and starting to use the iterator, you added arguments to the list that is to be iterated. This is a concurrent modification.

    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

Create the iterator AFTER you've finished adding elements to the list:

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

    ListIterator<String> it = s.listIterator();  
    if (it.hasNext())
        String item = it.next();
纸伞微斜 2024-08-13 03:08:48

来自 ConcurrentModificatoinException 的 JavaDoc:: “通常不允许一个线程修改集合,而另一个线程正在迭代它”。

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

From the JavaDoc: for ConcurrentModificatoinException: "it is not generally permssible for one thread to modify a Collection while another thread is iterating over it".

It simply means that if you still have an open iterator, you aren't allowed to modify the list because the iterator loop will break. Try moving ListIterator<String> it = s.listIterator(); till after the for loop.

往事风中埋 2024-08-13 03:08:48

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

You are not allowed to continue iterating over an iterator after the underlying list is modified. Here you create the iterator before adding a few items to s, and then proceed to do a hasNext() and a next() on it after the additions, leading to the ConcurrentModificationException

云巢 2024-08-13 03:08:48

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

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));
        }


    }
}

If the above solutions doesn't work properly. You can use old for-loop for iterating a List at the same time adding new items. See the example below:

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));
        }


    }
}
掩耳倾听 2024-08-13 03:08:48

要理解这一点,让我们看一下 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 的值 入口检查点
  • 当使用 put/get(添加/删除)时,
  • modCount 会递增nextEntry 迭代器的方法会使用当前 modCount 检查此值(如果它们不同),则抛出并发修改异常

以避免您可以:

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

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

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

有关 CopyOnWriteArrayList 的详细信息

to understand this lets look at source of HashMap implementation:

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

which contains HashIterator as below:

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

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

every time you create a iterator:

  • a counter expectedModCount is created and is set to value of modCount as entry checkpoint
  • modCount is incremented in cases of use put/get (add/remove)
  • nextEntry method of iterator is checking this value with current modCount if they are different concurrent modification exception is throw

to avoid this u can:

  • convert map to an array (not recommended for large maps)
  • use concurrency map or list classes (CopyOnWriteArrayList / ConcurrentMap)
  • lock map (this approach removes benefits of multithreading)

this will allow you to iterate and add or remove elements at the same time without rising an exception

Concurrency map/list iterator is a "weakly consistent" iterator that will
never throw ConcurrentModificationException, and guarantees to
traverse elements as they existed upon construction of the iterator,
and may (but is not guaranteed to) reflect any modifications
subsequent to construction.

More info on CopyOnWriteArrayList

Bonjour°[大白 2024-08-13 03:08:48

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 文档

ConcurrentModificationException may arise in both single threaded environment and multi-threaded environment.
The main catch is that all the general purpose iterators (like the one used in ArrayList) are all FailFast iterators, which fails when we try to modify one list if one iterator is already iterating over it.
Solution - > Use CopyOnWriteArrayList if such scenario is needed by the requirement rather than using ArrayList.

For a complete demo for this, below mentioned code can be used.
We just need to change the implementation from CopyOnWriteArrayList to 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;
    }

}

For more inifo follow this link this may be helpful alot ConcurrentModificationException Java Docs

孤寂小茶 2024-08-13 03:08:48

这不起作用:

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();

This didn't work:

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

This worked:

LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("aa");
linkedList.add("bb");
ListIterator listIterator = linkedList.listIterator();
与之呼应 2024-08-13 03:08:48

查看 oracle 文档 页面。

public class ConcurrentModificationException
extends RuntimeException

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

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

就您而言,您在创建迭代器后修改了集合,因此遇到了异常。

如果您按照 Stephen C 答案更改代码,你不会得到这个错误。

Have a look at oracle documentation page.

public class ConcurrentModificationException
extends RuntimeException

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.

In your case, you have modified the collection after creating the iterator and hence you have encountered the exception.

If you change your code as per Stephen C answer, you won't get this error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文