任何人都可以通过ConcurrentModificationException解释我吗?

情况1:这不会导致ConcurrentModificationException ? 任何人都可以告诉我为什么这不会导致ConcurrentModificationException。

 public class UpdatePeople { List  records = new ArrayList  (); public class AsyncTask extends AsyncTask  { List  people; public AsyncTask(List  allergy) { this.people = people; }@ Override protected Boolean doInBackground(Void...params) { List  responses = new ArrayList  (); for (People peopleList: this.people) { } } } } 

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

 public class UpdatePeople { List  records = new ArrayList  (); public class AsyncTask extends AsyncTask  { @ Override protected Boolean doInBackground(Void...params) { List  responses = new ArrayList  (); for (People peopleList: records) { } } } } 
  1. 任何人都可以解释我在case 1究竟发生了什么。 我无法理解这是如何解决ConcurrentModificationException问题的。
  2. 案例2是否将建议的实现从ArrayList更改为CopyOnWriteArrayList

添加例外:

05-28 20:34:21.073:E / XXX(904):未捕获的exception是: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):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):引起: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 c1 = new ArrayList(Arrays.asList(1, 2, 3)); for (Object o: c1) c1.remove(o); } } 

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

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

 final Iterator 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 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 )包装集合。 但请注意,在这种情况下,您仍然需要为迭代提供适当的锁定规则。 请参阅此答案了解详情。
  • 在将原始集合传递给后台作业之前制作副本(并确保后台作业是使用该副本的唯一线程)
  • 通过synchronized块保护集合的所有使用(例如,使用集合本身作为“monitor”对象,尽管更好:使用专用的监视器对象)。