我们的 Android 应用程序经常因 ConcurrentModificationException 而发生崩溃。
基本上,在我们的一个库中,它调用 org.apache.http.impl.client.BasicCookieStore 的 addCookie 方法并抛出 ConcurrentModificationException。这是相关的堆栈跟踪:
ArrayList.java line 569: java.util.ArrayList$ArrayListIterator.next
Collections.java line 960: java.util.Collections$UnmodifiableCollection$1.next
....
看起来 ConcurrentModificationException 被抛出,因为有 2 个(或更多)线程尝试访问 BasicCookieStore 类内部的数组列表。现在,考虑到 BasicCookieStore 类被标记为 ThreadSafe 并且所有数组列表访问方法似乎都是同步的。什么会导致这种情况?提示?
下面是BasicCookieStore的源代码供引用:source
最佳答案
这并不一定是由于线程安全性较弱造成的。当您为在迭代器到达其最终状态之前修改的结构调用 Iterator.next() 时,也可能会发生这种情况。即使在单线程中。 例如,此代码将抛出 ConcurrentModificationException:
ArrayList<Object> arrayList = new ArrayList<Object>();
arrayList.add(new Object());
arrayList.add(new Object());
arrayList.add(new Object());
//...
for (Object o : arrayList) { //iterating with iterator
arrayList.remove(0); // perform some modification while
//iterating over the structure
}
如果您研究 ArrayList 源代码,您会发现每次修改都会增加 int modCount
field 。当您通过 ArrayList.iterator() 创建迭代器时,它会拍摄 modCount 的快照并将其与当前列表的 modCount
进行比较。如果它们不相等,则每次迭代都会失败。
更新:我进行了调查,发现了一些问题代码BasicCookieStore
。我设法只找到了 ConcurrentModificationException
的一种可能性发生:您调用BasicCookieStore.toString()
在一个线程中,而某些修改(例如 addCookie()
)发生在另一个线程中。
此类对于快速失败迭代器来说几乎是安全的:所有方法都是 synchronized
除了toString()
。
让我们看一下代码:
@Override
public String toString() {
return cookies.toString();
}
它调用 ArrayList.toString()
:
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
您可以看到它使用了迭代器。所以考虑一下 toString()
执行后我们做一些修改(确实有可能是因为缺乏同步),例如addCookie
:
public synchronized void addCookie(Cookie cookie) {
if (cookie != null) {
// first remove any old cookie that is equivalent
for (Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
if (cookieComparator.compare(cookie, it.next()) == 0) {
it.remove();
break;
}
}
if (!cookie.isExpired(new Date())) {
cookies.add(cookie);
}
}
}
此方法通常不会经常对列表进行修改,但确实如此。
您可以亲自看到有可能增加 modCount
而toString
的迭代器未处于其终止状态。所以当它发生时 - toString
的iterator.next()
会抛出 ConcurrentModificationException
.
关于java - Android 的 Apache BasicCookieStore 上的 ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19101751/