并发修改异常,ConcurrentModificationException
ArrayList<Integer> arrayList2 = new ArrayList();
arrayList2.add(0,0);
arrayList2.add(1);
arrayList2.add(2);
arrayList2.add(3);
arrayList2.add(4);
for (Integer i : arrayList2) {
arrayList2.remove(2);
}
迭代器初始化时获取集合的modCount(修改次数)
int expectedModCount = modCount;
迭代器的next(),remove()方法会先去调用checkForComodification(),检测 modCount是否等于 expectedModCount,所以如果迭代器遍历的时候,只要集合发生修改就会抛出ConcurrentModificationException
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
迭代器的remove()方法在移除元素后会去更新expectedModCount,所以本迭代器不会抛出ConcurrentModificationException,每个迭代器实例有一个expectedModCount,所以本迭代器的remove()也会导致其他正在遍历的迭代器抛出ConcurrentModificationException
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();
}
}
有一种特殊情况修改后不会抛出ConcurrentModificationException
ArrayList<String> arrayList = new ArrayList();
arrayList.add("a");
arrayList.add("b");
arrayList.add("c");
arrayList.add("d");
arrayList.add("e");
//线程1在遍历的时候,线程1移除了倒数第二个元素"d"
//不仅没抛出异常,而且会遍历漏掉最后一个元素
Thread t1 = new Thread(()->{
for (String str:arrayList){
if(str.equals("d")){
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(str);
}
}
},"t1");
Thread t2 = new Thread(()->{
Iterator<String> iterator = arrayList.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if(str.equals("d")){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//移除
arrayList.remove(str);
}
}
},"t2");
t1.start();
t2.start();
原因:迭代器hasNext()判断的是cursor != size,删除倒数第二个元素,这时候cursor刚好等于size,hasNext()会返回false,直接结束了遍历,没有机会走到next()中判断modCount是否发生变化。
public boolean hasNext() {
return cursor != size;
}
同一线程也会漏
public static void main(String[] args){
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String str = (String) iterator.next();
if(str.equals("d")){
list.remove(str);
}else{
System.out.println(str);
}
}
}
ArrayList等java.util包下的集合,设计初衷就不是线程安全的,所以在发生未知修改的时候,抛出ConcurrentModificationException,如果不抛出ConcurrentModificationException,发生修改后导致size发生变化很容易抛出IndexOutOfBoundsException、NoSuchElementException错误。