1.背景(增强for循环中即使没有并发修改异常,但是也会抛出这个错误)
package com.ydlclass.collection;
import java.util.*;
public class ConcurrentModityTest {
public static void main(String[] args) {
test1();
}
public static void test1(){
List<String> names = new ArrayList<>();
Collections.addAll(names,"ssc","ssd","lili");
for (String name : names) {//这种方式会导致并发修改的问题,从而抛出并发修改异常(即时没有并发也会抛出异常)
if ("lili".equals(name)){
names.remove(name);
}
}
}
}
2.多线程并发修改的情况:
package com.ydlclass.collection;
import java.util.*;
public class ConcurrentModityTest {
public static void main(String[] args) {
test1();
}
public static void test1(){
final List<Integer> nums = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
nums.add(new Random().nextInt(1000000000));
}
for (int i = 0; i < 20; i++) {//多线程并发修改;
new Thread(()->{
Iterator<Integer> iterator = nums.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
if (next > 1000000){
iterator.remove();
}
}
}).start();
}
}
}
3.出现问题的原因在于:
3.1由于遍历是通过size()和游标来实现的,假设两个线程,其中一个已经获得了size,但是遍历期间被另个线程删除了元素,导致实际的size小于原先获取的,就会产生数据越界异常。从而抛出并发修改异常;
3.2CheckForModifyCount()会比较modCount与expectedModCount,前者是父类AbstractList的成员变量,后者是内部类迭代器的成员变量,所属的对象不同;方法中会比较两个值是否相同,modCount是每个线程都能获得的,expectedModCount是每个线程迭代器的期望修改次数,多线程修改共享变量,不能保证多线程原子序,有序性,可见性。就会产生问题;(迭代器首先让expectedModCount等于modCount,在remove方法中结束的时候在此执行前面的赋值操作,)
4.分析原因:
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() {//next()方法或者是hasNext()方法都会先调用这个方法
//判断期望的的修改次数是否等于实际的修改次数(实际的修改次数是父类的成员变量)
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
Note:多线程的情况下,假设线程A的迭代器中的成员变量exceptedModCount = 1 ,迭代器a遍历下次之前有其他线程(线程B的迭代器b)修改会让modCount变为2,此时线程A想要执行next方法时,由于判断了自己维护的exppectedModCount = 1 不等于实际的modCount了,就会抛出异常。
4.解决的办法,使用迭代器remove方法删除;(虽然迭代器的remove 方法中同样使用了ArrayList的remove方法,但是迭代器中多了一部分内容,其中最终要的就是在执行完成remove方法之后让expectedModCount = modCount就会使得期望值和实际值之间保持一致,即使是其他的线程修改了也不会影响)
5.总结:不允许在增强for循环使用对象直接调用remove方法删除元素,也不允许迭代器中使用对象直接调用remove方法删除元素;可以使用迭代器的remove方法删除元素才不会抛出并发修改异常;