4

案例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): 在 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): at 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): 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): 由: java.util.ConcurrentModificationException 05-28 20:34:21.073: E/XXX(904): 在 java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)ArrayList$ArrayListIterator.next(ArrayList.java:569)ArrayList$ArrayListIterator.next(ArrayList.java:569)

4

1 回答 1

8

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

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

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

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

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

如果基础集合是 a List,您可以通过使用 aListIterator而不是普通迭代器来解决此限制:

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迭代线程中的 s,而且还会破坏集合内部数据的完整性结构。

您可以做几件事:

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