ArrayList之坑点,ArrayList之坑
分享于 点击 30139 次 点评:65
ArrayList之坑点,ArrayList之坑
ConcurrentModificationException异常
详细原因参见 http://www.cnblogs.com/dolphin0520/p/3933551.html
缘起iterator
简单来说:在iterator中遍历以及删除时都会去执行如下检查
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount 就是modify count 对队列修改的次数。
那么expectedModCount是什么时候赋予的呢?
public Iterator<E> 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的情况,从而导致异常产生。
仅仅只是iterator?牵出foreach
被广泛使用的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();
}
}
这个只能说解决了一小部分问题。遇到多线程还是有问题。
最后有2种解决办法:
1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;
用这种同步锁的方式来阻止modCount不一致的情况发生。这个办法直接,但是需要自己手动添加锁。
2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。见http://blog.csdn.net/xude1985/article/details/51418558
addAll
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只是浅拷贝而已,并没有进行深拷贝,所以你在改变对象内部数据的时候,仍旧是一变都变的
add
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add时候并没有给你进行去重,所以你需要在调用add前自己去重
相关文章
- 暂无相关文章
用户点评