【问题标题】:Can anyone explain me over ConcurrentModificationException?谁能解释一下 ConcurrentModificationException 吗?
【发布时间】:2013-05-23 14:34:38
【问题描述】:

案例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. 谁能解释一下case 1 中到底发生了什么。我无法理解这是如何解决 ConcurrentModificationException 问题的。
  2. 案例 2 是否建议将实现从 ArrayList 更改为 CopyOnWriteArrayList

添加例外:

05-28 20:34:21.073:E/XXX(904):未捕获的异常是:05-28 20:34:21.073: E/XXX(904): java.lang.RuntimeException: 发生错误 在执行 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):在 java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 05-28 20:34:21.073: E/XXX(904): 在 java.util.concurrent.FutureTask.setException(FutureTask.java:124) 05-28 20:34:21.073: E/XXX(904): 在 java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 05-28 20:34:21.073: E/XXX(904): 在 java.util.concurrent.FutureTask.run(FutureTask.java:137) 05-28 20:34:21.073: E/XXX(904): 在 android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 05-28 20:34:21.073: E/XXX(904): 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 05-28 20:34:21.073: E/XXX(904): 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 05-28 20:34:21.073: E/XXX(904): 在 java.lang.Thread.run(Thread.java:856) 05-28 20:34:21.073: E/XXX(904): 引起:java.util.ConcurrentModificationException 05-28 20:34:21.073: E/XXX(904): 在 java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)

【问题讨论】:

  • 请将异常消息和“for (People peopleList: records)”循环内的代码添加到您的问题中
  • @mschenk74:我得到的第二种情况的异常是(People peopleList:records)的循环
  • 当有一个迭代器在它上面时,你因为修改一个对象而受到了打击。
  • @HotLicks 但问题是为什么它不会在第一种情况下发生
  • 在案例 1 中,您遍历 AsyncTask 本地的 people(至少参考是)。在情况 2 中,您迭代 records,它将由您同时运行的尽可能多的 AsyncTask 共享。可能是这个原因吗?

标签: java


【解决方案1】:

一般来说,如果您在迭代集合时尝试修改集合,则会引发 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

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

你可以做一些事情:

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

【讨论】:

  • 请准确地说,在使用 itr.remove()、itr.add() 迭代集合时仍然可以修改集合
猜你喜欢
  • 2015-11-23
  • 1970-01-01
  • 1970-01-01
  • 2017-04-07
  • 2015-12-21
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多