首先,有一段代码引出我们的问题
List<String> list=new ArrayList<>();
list.add("靳卓1");
list.add("靳卓2");
list.add("大帅逼");
System.out.println(list);
for(String s:list){
if("靳卓1".equals(s)){
list.remove(s);
}
}
Iterator it =list.iterator();
while(it.hasNext()){
String str=(String) it.next();
if("靳卓1".equals(str)){
it.remove();
//list.remove("靳卓1");//此种同增强for循环
}
}
我们在使用增强for循环进行循环,然后删除某个元素时会出现异常ConcurrentModificationException,我们在使用迭代器循环使用list删除时同样会出现这样的问题,因为增强for循环其实使用的就是迭代器,但是我们使用迭代器删除,就不会报错,下面我们看一下ArrayList的remove方法
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
删除时首先检查index下标是否越界,然后有一个modCount++操作,modCount是我们对list修改的次数,然后我们去查看Iterator it =list.iterator() 的iterator方法,这是ArrayList获取迭代器的方法。
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.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;
Itr() {}
在这里return new itr(),itr是ArrayList的一个内部类,实现了Iterator接口,我们循环时需要使用到迭代器的next()方法
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
这就是next方法逻辑,第一行有一个checkForComodification方法,点进去
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
到这里我们就知道为什么为什么会ConcurrentModificationException异常了,我们在使用Arraylist的remove方法是进行了modcount++操作,导致modCount != expectedModCount,抛出了这个异常,但是使用迭代器的remove不会出现这个异常,查看迭代器的remove方法
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();
}
}
注意有一个expectedModCount = modCount操作,因此使用迭代器时保证了modCount和expectedModCount想等,不会出现问题,但是在多线程情况下即使使用迭代器的remove方法删除依然会出现这个异常,其实上面我们已经说到了,arraylist迭代器的获取每次都是新创建的一个迭代器对象,但是他们操作的集合是同一个集合,也就是多个线程对同一个arraylist集合元素进行删除,但是他们的迭代器对象是不同的,因此一个线程调取remove方法进行了 modcount++以及expectedModCount = modCount操作,但是另一个线程并没有expectedModCount = modCount这个过程,所以仍然会抛出异常,可以使用CopyOnWriteArrayList。
public Iterator<E> iterator() {
return new Itr();
}
查看CopyOnWriteArrayList的remove方法
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
可以看到使用了ReentrantLock加锁操作,因此自然不会出现多线程并发修改的问题,注意在这里是使用的CopyOnWriteArrayList的remove方法,不是CopyOnWriteArrayList迭代器的remove方法。此外我们可以看到在remove的时候是创建了一个新数组,在新数组上进行修改,修改完毕后讲新数组设置为CopyOnWriteArrayList的原数组,我们可以进一步查看CopyOnWriteArrayList的get方法
private E get(Object[] a, int index) {
return (E) a[index];
}
get方法没有加锁,因此可以读写不互斥,修改在新数组上修改,读取时读取旧数组。
从ArrayList到CopyOnWriteArrayList
原文:https://www.cnblogs.com/blosjin/p/13431942.html