关于List的ConcurrentModificationException异常,想必大家都遇到过。一般都是在遍历集合时,对其进行了删除操作。大家都知道使用for循环替代foreach即可避免此问题。
今天遇到了另一种情况的ConcurrentModificationException异常,在此记录下。
看下下面的代码:
List<Info> Lists = ConfigAPI.getList();
Log.d(TAG, "lists:" + Lists);
就是这短短的两句话,就抛出了ConcurrentModificationException异常。大家看到这里想必和我一样疑惑,这怎么会有问题??我又修改。其实分析一下源码来看看。
报的异常信息可以方便我们快速定位,异常信息如下:
at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
at java.util.AbstractCollection.toString(AbstractCollection.java:372)
at java.lang.StringBuilder.append(StringBuilder.java:202)
通过日志我们知道,打印时会调用集合的toString方法,把集合转成字符串,在AbstractorCollection的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方法中,调用了迭代器的next方法。然后modCount不等于expectedModCount,抛出异常。
然后再结合代码分析,在多线程的情况下,线程A在执行完获取集合并开始打印时,通过上面分析我们知道,打印时会遍历集合。线程B又开始执行获取集合的操作,由于引用是同一个,线程A和线程B拿到的是同一个list的引用,当线程A对集合进行遍历时,线程B对集合进行了修改。此时触发线程A抛异常。
分析了这些,解决方案也就有了。就是每次都new一个集合,这样多线程下也不是同一个引用了。
方案如下:
List<Info> Lists = new ArrayList();
lists.addAll(ConfigAPI.getList());