首页我们要了解,在一些集合类中,如ArrayList,LinkedList,HashMap等都有一个参数叫modCount,顾名思义为修改次数,当你对集合进行add,remove等操作时,modCount都会+1,看上述类的源码都能看到,下面贴下ArrayList进行修改操作时,modCount值的变化.
//ArrayList的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//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;
}
//ArrayList的clear方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
当我们使用迭代器(iterator)对集合对象进行遍历的时候,如果 A 线程正在对集合进行遍历,此时 B 线程对集合进行修改(增加、删除、修改),或者 A 线程在遍历过程中对集合进行修改,都会导致 A 线程抛出 ConcurrentModificationException 异常。
重点: 只有当使用迭代器进行遍历进行修改操作时,才会发生ConcurrentModificationException,for-i方式的遍历是不会抛出上述错误的
public static void main(String[] args) {
//init
List<Integer> list = new ArrayList<Integer>(){{
add(1);
add(2);
add(3);
}};
//注意for循环,编译过后本质就是iterator,for循环只是java的一种语法糖
//编译后的文件如下:
//Iterator var3 = list.iterator();
//while(var3.hasNext()) {
// Integer item = (Integer)var3.next();
// list.remove(item);
// System.out.println(item);
//}
for (Integer item : list) {
list.remove(item);
}
}
运行如上代码,会报下述异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)
at com.example.demo.FastFailDemo.main(FastFailDemo.java:66)
原因是使用迭代器进行遍历时(iterator方法和listIterator方法),每次都会去判断modCount的值是否与期望的一致,不一致就会抛出ConcurrentModificationException,源码如下:
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
而这就是所谓的fail-fast机制,源码中有对modCount这个变量的介绍,直接贴源码内的备注
/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
*
* <p>This field is used by the iterator and list iterator implementation
* returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list
* iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous},
* {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in
* the face of concurrent modification during iteration.
*
* <p><b>Use of this field by subclasses is optional.</b> If a subclass
* wishes to provide fail-fast iterators (and list iterators), then it
* merely has to increment this field in its {@code add(int, E)} and
* {@code remove(int)} methods (and any other methods that it overrides
* that result in structural modifications to the list). A single call to
* {@code add(int, E)} or {@code remove(int)} must add no more than
* one to this field, or the iterators (and list iterators) will throw
* bogus {@code ConcurrentModificationExceptions}. If an implementation
* does not wish to provide fail-fast iterators, this field may be
* ignored.
*/
protected transient int modCount = 0;
翻译如下:
此列表在结构上被修改的次数。结构修改是那些改变列表大小的修改,或者以其他方式扰乱它,使得正在进行的迭代可能产生不正确的结果。该字段由 iterator 和 listIterator 方法返回的迭代器和列表迭代器实现使用。如果此字段的值意外更改,迭代器(或列表迭代器)将抛出 ConcurrentModificationException 以响应下一个、删除、上一个、设置或添加操作。这提供了快速失败的行为,而不是面对迭代期间的并发修改时的不确定行为。子类对该字段的使用是可选的。如果一个子类希望提供快速失败的迭代器(和列表迭代器),那么它只需要在其 add(int, E) 和 remove(int) 方法(以及它覆盖的任何其他导致结构列表的修改)。对 add(int, E) 或 remove(int) 的单个调用必须向该字段添加不超过一个,否则迭代器(和列表迭代器)将抛出虚假的 ConcurrentModificationExceptions。如果实现不希望提供快速失败的迭代器,则可以忽略此字段。