它不一定是由弱线程安全引起的。当您为在迭代器到达其终端状态之前修改的结构调用 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
字段。当您通过 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
' 的迭代器未处于其终端状态时,有可能增加。所以当它发生时 -'stoString
会iterator.next()
抛出一个ConcurrentModificationException
.