目录
并发修改异常简介
-  什么是并发修改异常 -  并发的意思是同时发生,并发修改的意思是在同时发生某个时间时去修改。 
-  并发修改异常的原因是:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。 
 
-  
-  场景:当我们在对集合进行迭代遍历操作的时候,如果同时对集合对象中的元素进行某些操作(增删),则容易导致并发修改异常的产生。 
异常展示
-  我们创建一个ArrayList数组,当数组中有“world”元素时,我们增加一个“javaee”元素 
import java.util.ArrayList;
import java.util.Iterator;
public class TestDemo {
    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<>();
        //创建并添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
        Iterator it = array.iterator();
        while (it.hasNext()){
            String s =(String) it.next();
            if ("world".equals(s)){
                array.add("javaee");
            }
        }
    }
}报错原因:Exception in thread "main" java.util.ConcurrentModificationException (并发 修改异常)
异常原因
-  通过控制台找到问题产生的根源 Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)//问题根源 at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967) at TestDemo.main(TestDemo.java:15)
-  查看根源方法代码 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException();//抛出并发异常 }
-  原因:由异常根源的方法内容得知,当一个变量modCouunt 不等于 expectedModCount变量时,会抛出此异常 
异常探究
接下来探究modCount与expectedModCount具体代表什么
-  modCount -  在抽象类AbstractList定义了modCount变量,而作为他的子类ArrayList继承到了modCount这个变量 
-  源码中对于modCount的解释 
 
-  
-  expectedModCount -  当ArrayList对象调用iteroter()方法时,会创建内部类Itr的对象 
-  Itr的源码 
 private class Itr implements Iterator<E> { int cursor; int expectedModCount = modCount; // .... }-  由此段代码可知:当ArrayList对象调用iteroter()方法时,会将expectedModCount的值会等于modCount变量的值。 
 
-  
-  modCount是如何变化的? -  当我们创建ArrayList对象的时候,ArrayList对象里包含了此变量modCount并且初始化值为0; 
-  通过查看源码,我们发现ArrayList中的添加删除操作的方法,都会使modCount自增一次,例如删除的源码 
 public E remove(int index) { rangeCheck(index); modCount++; //modCount自增 ...//此处省略代码 }-  每次都集合元素的个数产生变化时,modCount的值都会+1,modCount变量就是记录了对集合元素个数的改变次数 
 
-  
异常流程
分析完modCount与expectedModCount两个变量,我们再来结合迭代器的工作流程来分析异常出现的过程
-  迭代器的创建 -  当ArrayList对象调用iteroter()方法时,会创建内部类Itr的对象,此时迭代器对象中有两个最关键的成员变量:cursor、expectedModCount 
 private class Itr implements Iterator<E> { int cursor; // 集合中元素的索引 int expectedModCount = modCount; } -  cursor -  迭代器工作就是将集合中的元素逐个取出,而cursor就是迭代器中用于指向集合中某个元素的指针 
-  在迭代器迭代的过程中,cursor初始值为0,每次取出一个元素,cursor值会+1,以便下一次能指向下一个元素,直到cursor值等于集合的长度为止,从而达到取出所有元素的效果。 
 
-  
-  expectedModCount -  expectedModCount在迭代器对象创建时被赋值为modCount 
-  在迭代集合元素的过程中,迭代器通过检查expectedModCount和modCount的值是否相同,以防止出现并发修改。 
 
-  
 
-  
-  迭代器迭代过程 -  在使用迭代器时,都会使用迭代器的hasNext()方法判断是否还有下一个元素,源码 
 public boolean hasNext() { return cursor != size; }-  当我们hasNext()返回true下一个元素仍有时,我们就会使用next()方法去取出元素,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()方法时,第一行就调用了我们最开始异常根源的方法。 
 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }所以我们最终得知: -  当迭代器通过next()方法返回元素之前都会检查集合中的modCount和最初赋值给迭代器的expectedModCount是否相等,如果不等,则抛出并发修改异常。 
-  也就说,当迭代器工作的过程中,不允许集合擅自修改集合结构,如果修改了会导致modCount值变化,从而不会等于expectedModCount,那么迭代器就会抛出并发修改异常 
 
-  
并发修改异常出现的意义
-  我们通过上文的分析其实可以知道,迭代器是通过cursor指针指向对应集合元素来挨个获取集合中元素的,每次获取对应元素后cursor值+1指向下一个元素,直到集合最后一个元素。 
-  那么如果在迭代器获取元素的过程中,集合中元素的个数突然改变,那么下一次获取元素时,cursor能否正确的指向集合的下一个元素就变得未知了,这种不确定性有可能导致迭代器工作出现意想不到的问题。 
-  为了防止在将来某个时间任意发生不确定行为的风险,我们在使用迭代器的过程中不允许修改集合结构(也可以说是不允许修改元素个数),否则迭代器会抛出异常结束程序。 
并发修改的解决方案
我们既然不能使用迭代器遍历的,集合修改元素的方式,我们可以
-  1.迭代器遍历,迭代器增删元素。-  迭代器中提供了增删的方法,且对cursor进行了处理,Iterator迭代器却没有添加功能,所以我们使用其子接口ListIterator 
-  例如 获取Itr迭代器的子类对象ListItr,ListItr中有添加元素的add方法: 
 public void add(E e) { checkForComodification();  try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; //对cursor进行处理防止出现问题 lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
-  
-  解决方案1:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class TestDemo {
    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<>();
        //创建并添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
        //Iterator迭代器却没有添加功能,所以我们使用其子接口ListIterator
        ListIterator lit = array.listIterator();
        while(lit.hasNext()){
            String s = (String) lit.next();
            if("world".equals(s)){
                lit.add("javaee");
            }
        }
        System.out.println("array:"+array);
    }
}
-  集合遍历元素,集合增删元素-  属于List体系的集合我们可以使用用普通for循环,通过索引获取集合元素的方法来遍历集合,这个时候修改集合结构是不会出现异常的。 
 
-  
-  解决方案2
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class TestDemo {
    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<>();
        //创建并添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
        for(int i = 0;i < array.size();i++){
            String element = array.get(i);
            if(element.equals("java")){
                array.add("javaee");
            }
        }
        System.out.println("array:"+array);
    }
}









