java - Android 的 Apache BasicCookieStore 上的 ConcurrentModificationException

标签 java android apache concurrency crash

我们的 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);
        }
    }
}

此方法通常不会经常对列表进行修改,但确实如此。 您可以亲自看到有可能增加 modCounttoString的迭代器未处于其终止状态。所以当它发生时 - toStringiterator.next()会抛出 ConcurrentModificationException .

关于java - Android 的 Apache BasicCookieStore 上的 ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19101751/

相关文章:

java - hibernate 5 + Glassfish 4.1.1 : java. lang.NoSuchMethodError : org. jboss.logging.Logger.debugf(Ljava/lang/String;I)V

android - 来自 maven 的 aar 导致 ClassNotFoundException。不包括 aar 的依赖项

android - 如何将 SUM 和 DINSTINCT 与 GreenDao 查询生成器一起使用?

android - RecyclerView 上下滚动时滚动问题数据已更改

security - .htaccess 密码保护有多安全?

java - 通过反射获取在导入类中声明的枚举常量

java - System.out如何实现字符的控制台输出?

java - 在 iReport 中初始化 HashMap 变量

android - 如何为每个应用程序使用一个 HttpClient?

php - Magento:创建发票时出现 500 内部服务器错误