情况 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
中到底发生了什么。我无法理解这如何解决ConcurrentModificationException
问题。 - 是否推荐案例 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/