Exception in thread "Thread-1" java.util.ConcurrentModificationException 异常缘由和解决方法

基本上全部的集合类都会有一个叫作快速失败的校验机制,当一个集合在被多个线程修改并访问时,就会出现ConcurrentModificationException 校验机制。它的实现原理就是咱们常常提到的modCount修改计数器。若是在读列表时,modCount发生变化则会抛出ConcurrentModificationException异常。这与线程同步是两码事,线程同步是为了保护集合中的数据不被脏读、脏写而设置的。java


一、单线程环境下的异常重现安全

    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2) {
                list.remove(integer);
                //iterator.remove();   //正确写法
            }
        }
    }

  在while(iterator.hasNext()) 循环遍历时,只容许删除ArrayList 内部的  elementData[ ] 的最后一个元素,而不容许从中间删除。
  在 iterator.next()  的源码中,会首先执行方法:checkForComodification();  该方法:多线程

     final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

  只有当两个变量值相等时才不会报错。而 list.add(2)操做和 list.remove(integer); 操做会使 modCount++;   可是变量 expectedModCount 是内部类的 Itr 子类 的 变量,该子类的实例化方法是:list.iterator();  实例对象时赋初始值:
int expectedModCount = modCount;   因此,不容许在 while(iterator.hasNext())  循环方法内 有list.add(2)操做和 list.remove(integer);操做,不然会致使expectedModCount != modCount ,进而致使  throw new ConcurrentModificationException();并发


二、多线程下的问题重现:
public class Vector {
    public static void main(String[] args) {
        final List<String> tickets = new ArrayList<String>();
        for(int i=0;i<100000;i++){
            tickets.add("火车票"+i);
        }
        Thread returnThread = new Thread(){
            @Override
            public void run() {
               while (true){
                   tickets.add("车票"+ new Random().nextInt());
               }
            };
        };
        Thread saleThread = new Thread(){
            @Override
            public void run() {
                for (String ticket : tickets){
                    tickets.remove(0);
                }
            };
        };
        returnThread.start();
        saleThread.start();
    }
}

  

  有可能有朋友说ArrayList是非线程安全的容器,换成Vector就没问题了,实际上换成Vector仍是会出现这种错误。dom

  缘由在于,虽然Vector的方法采用了synchronized进行了同步,可是实际上经过Iterator访问的状况下,每一个线程里面返回的是不一样的iterator,也便是说expectedModCount是每一个线程私有。倘若此时有2个线程,线程1在进行遍历,线程2在进行修改,那么颇有可能致使线程2修改后致使Vector中的modCount自增了,线程2的expectedModCount也自增了,可是线程1的expectedModCount没有自增,此时线程1遍历时就会出现expectedModCount不等于modCount的状况了。ide

  所以通常有2种解决办法:线程

  1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;对象

  2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。blog

相关文章
相关标签/搜索