并发修改错误-单线程情况下
文章目录
public class CMETest {
public static void main(String[] args) {
Vector<Integer> vct = new Vector<>();
for(int i=0; i < 10; i++) {
vct.add(i);
}
Iterator<Integer> ite = vct.iterator();
vct.remove(0); // 这里是错误的根源
Integer x = ite.next(); // 报错位置
System.out.println(vct);
}
}
"==========报错信息==========="
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.Vector$Itr.checkForComodification(Vector.java:1184)
at java.util.Vector$Itr.next(Vector.java:1137)
at cn.edu.lirui18.demo11.CMETest.main(CMETest.java:17)
分析vector.java
的1137行代码,以下截取了片段代码。
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;
public boolean hasNext() {
return cursor != elementCount;
}
public E next() {
synchronized (Vector.this) {
checkForComodification(); // 1137
int i = cursor;
if (i >= elementCount)
throw new NoSuchElementException();
cursor = i + 1;
return elementData(lastRet = i);
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
synchronized (Vector.this) {
checkForComodification();
Vector.this.remove(lastRet);
expectedModCount = modCount;
}
cursor = lastRet;
lastRet = -1;
}
final void checkForComodification() { // ConcurrentModificationException
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
- 首先
Itr
是Vector
的私有类,Itr
中能够访问Vector
的成员变量modCount
; modCount
计数了容器的修改次数,包括了如下的修改:排序、增删、替换、长度扩容等;Itr
的成员变量expectedModCount
在迭代器对象构造的时候赋值为modCount
,之后执行next
操作会检查expectedModCount==modCount
,如果为假,那么也就是外部的modCount
值改变了,也就是容器发生了改变。- 一旦容器发生了改变,迭代器通过方法
checkForComodification
察觉后,抛出ConcurrentModificationException
。
总结:也就是在使用迭代器的时候,不能直接修改容器,而只能通过迭代器的修改方法进行修改,在该Itr
类中,只提供了remove
方法(也没见过迭代器提供其他的修改容器的方法)。synchronized
代码块定义了同步语句块,防止多个线程同时访问、修改。
补:
- 在
itr
内访问Vector.this
是访问的Itr
的外部的Vector
对象。 - 可以看到
Vector
的迭代器是线程同步的,所以是线程安全的,而ArrayList
不是,其迭代器部分代码如下。
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;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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];
}
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();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}