详细原因参见 http://www.cnblogs.com/dolphin0520/p/3933551.html
简单来说:在iterator中遍历以及删除时都会去执行如下检查
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount 就是modify count 对队列修改的次数。
那么expectedModCount是什么时候赋予的呢?
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
在Iterator对象初始化的时候就赋予了当前的modCount值,如果这之后在执行iterator.next() 的同时,还去调用list.add或remove操作,那么肯定会出现expectedModCount != modCount的情况,从而导致异常产生。
被广泛使用的foreach,其实间接的就是使用iterator实现的。(在JDK中没法看到具体实现,反编译可以看到字节码窥探到) 参见 http://blog.csdn.net/a596620989/article/details/6930479
在遍历的时候他不希望队列在别的线程里还进行队列的修改,也就是说在设计的时候没有考虑多线程同步问题,然后用这种条件来限制。
在删除的时候使用iterator的remove ,因为他进行删除的时候,会同步一下modCount的值给expectedModCount。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
这个只能说解决了一小部分问题。遇到多线程还是有问题。
用这种同步锁的方式来阻止modCount不一致的情况发生。这个办法直接,但是需要自己手动添加锁。
public boolean addAll(Collection extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
虽然我们看到有arraycopy,但是他复制的只是对象引用,而作为被指向的目标:对象数据则是没有任何改变。addAll只是浅拷贝而已,并没有进行深拷贝,所以你在改变对象内部数据的时候,仍旧是一变都变的
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add时候并没有给你进行去重,所以你需要在调用add前自己去重