java - 谁能解释一下 ConcurrentModificationException?

标签 java

情况 1:这不会导致 ConcurrentModificationException?。谁能告诉我为什么这不会导致 ConcurrentModificationException。

public class UpdatePeople {
    List < People > records = new ArrayList < People > ();

    public class AsyncTask extends AsyncTask < Void, Void, Boolean > {
        List < People > people;

        public AsyncTask(List < People > allergy) {
            this.people = people;
        }@
        Override
        protected Boolean doInBackground(Void...params) {
            List < String > responses = new ArrayList < String > ();
            for (People peopleList: this.people) {

            }

        }

    }
}

情况 2:这会导致 ConcurrentModificationException,因为我试图访问我的 AsyncThread 中的人员列表,这不是线程安全的。我可以让我的人员列表实现线程安全的 CopyOnWriteArrayList,这应该可以工作。

public class UpdatePeople {
        List < People > records = new ArrayList < People > ();

        public class AsyncTask extends AsyncTask < Void, Void, Boolean > {
            @
            Override
            protected Boolean doInBackground(Void...params) {
                List < String > responses = new ArrayList < String > ();
                for (People peopleList: records) {

                }

            }

        }
    }
  1. 谁能给我解释一下案例 1 中到底发生了什么。我无法理解这如何解决 ConcurrentModificationException 问题。
  2. 是否推荐案例 2 将实现从 ArrayList 更改为 CopyOnWriteArrayList

添加异常(exception):

05-28 20:34:21.073: E/XXX(904): Uncaught exception is: 05-28 20:34:21.073: E/XXX(904): java.lang.RuntimeException: An error occured while executing doInBackground() 05-28 20:34:21.073: E/XXX(904): at android.os.AsyncTask$3.done(AsyncTask.java:299) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask.run(FutureTask.java:137) 05-28 20:34:21.073: E/XXX(904): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 05-28 20:34:21.073: E/XXX(904): at java.lang.Thread.run(Thread.java:856) 05-28 20:34:21.073: E/XXX(904): Caused by: java.util.ConcurrentModificationException 05-28 20:34:21.073: E/XXX(904): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)

最佳答案

一般来说,如果您尝试在遍历集合时修改集合,则会引发 ConcurrentModificationException。例如:

public class Main {

    public static void main(String[] args) 
    throws Exception {

        final List<Object> c1 = new ArrayList<Object>(Arrays.<Object>asList(1, 2, 3));
        for (Object o: c1) c1.remove(o);
    }
}

将在运行时导致 java.util.ConcurrentModificationException,因为我们在迭代列表时修改列表(注意:这里只涉及一个线程)。这由列表的迭代器检测到并导致异常。

如果所需的修改是删除刚从迭代器接收到的元素,您可以通过使用 iterator directly 获得所需的结果(在单线程情况下!) .将 for(each) 循环替换为:

final Iterator<Object> iterator = c1.iterator();

while (iterator.hasNext()) {

    final Object o = iterator.next();
    if (satisfiesSomeCriterion(o)) iterator.remove();
}

然而,这并不适用于所有修改。例如,添加将不起作用。以下代码仍然失败:

for (Object o: c1) c1.add(Integer.valueOf(((Integer)o).intValue() + 10));

如果底层集合是 List,您可以使用 ListIterator 而不是普通迭代器来解决此限制:

final ListIterator<Integer> iterator = c1.listIterator();

while (iterator.hasNext()) {

    final Integer o = iterator.next();
    if (satisfiesSomeCriterion(o)) iterator.add(Integer.valueOf(o.intValue() + 10);
}

但请注意,这不等同于上面给出的代码(插入元素的其他位置)。另请注意,ListIterator.add 是一个可选方法;调用实现时可能会抛出 UnsupportedOperationException

上述所有内容仅适用于单线程情况。如果您尝试在没有适当同步的情况下同时从多个线程访问同一个集合,就会出现一组全新的问题,因为这不仅有可能在迭代线程中导致 ConcurrentModificationException,而且破坏集合内部数据结构的完整性。

您可以做一些事情:

  • 使用并发感知集合(例如您已经提到的CopyOnWriteArrayList)
  • 通过 Collections.synchronizedList 包装集合(...Set...Whatever)。但是请注意,在这种情况下,您仍然有责任为迭代提供适当的锁定规则。参见 this answer了解详情。
  • 在将原始集合传递给后台作业之前制作一份副本(并确保后台作业是唯一使用该副本的线程)
  • 通过 synchronized block 保护集合的所有使用(例如,使用集合本身作为“监视器”对象,但更好:使用专用监视器对象)。

关于java - 谁能解释一下 ConcurrentModificationException?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16795151/

相关文章:

java - android ArrayList在getter方法之后为空

java - 如何在android中将 map 截图保存到数据库中

java - 正则表达式删除 Java 中括号周围的空格

java - 如何让 Spinner OnListItemSelected 工作

java - 在 Java 中整齐地格式化 arraylist 的输出列

java - 无法读取jsp中的bean属性

java - Recycler添加监听器onScrollStateChanged,回调滚动状态 "SCROLL_STATE_IDLE",延迟时间不可忽略

java - 在 servlet 的 jsp 页面中显示图像

java - Spring hibernate @param 将映射到什么

java - 在java中添加数字中存在的所有质数数字。这段代码有什么错误?