Java 并发修改异常怎么破?直接撸源码

本文深入解析并发修改异常(ConcurrentModificationException),通过示例代码详细阐述其触发原因,包括实际修改次数与预期修改次数不符的情况,并提供解决方案。

1.何为并发修改异常

首先我们需要看一段代码,如下新建Demo类:

public class Demo {
 	public static void main(String[] args) {
  		ArrayList<String> list = new ArrayList<String>();
  		list.add("I");
  		list.add("love");
  		list.add("java");
  		//使用Iterator迭代器对list进行迭代
  		Iterator it = list.iterator();
  		while (it.hasNext()) {
  			if (it.next().equals("java")) {
    				list.add("very");
   			}
  		}
  		System.out.println(list);
  	}
}

运行结果:
在这里插入图片描述

  根据第一行的提示,我们可以看出这是个叫ConcurrentModificationException的异常,翻译过来就是并发修改异常,很好,现在我们去java帮助文档查看该异常的官方描述,如下:
在这里插入图片描述
  看官方的描述似乎还是一头雾水?好吧,通俗的来讲有一块地种了些庄稼,现在有个人过来施肥,他可以根据庄稼的数量计算带多少肥料正好。但现在又冒出来个种地的,施肥的人需要和他一块工作,那么施肥的人该带多少肥料呢?
  这里还要求我们注意,就算是一个人如果不好好按照要求种地(乱调方法),也会导致该异常。
  弄明白这个异常什么意思后,现在我们需要从源码的角度去搞清楚这个异常是怎么触发的。

2.源码

  根据异常的提示信息我们可以看到在ArrayList下有一个Itr的内部类,该内部类中的checkForComodification()方法出了问题,现在我们把相关代码拿过来,同时删去我们暂时不需要的源代码

public class ArrayList<E> extends AbstractList<E> implements List<E>{
	private static final int DEFAULT_CAPACITY = 10;//生成的ArrayList对象默认长度
	private static final Object[] EMPTY_ELEMENTDATA = {};//空的Object数组
	private transient Object[] elementData;//装ArrayList元素的数组
	private int size;//ArrayList的元素数量
	//因为ArrayList实现了List接口,所以要实现List中的Iterator方法和add方法
	//Iterator()方法
	 public Iterator<E> iterator() {
        	return new Itr();
    	}
    	//add方法
    	public boolean add(E e) {
        	ensureCapacityInternal(size + 1);  // Increments modCount!!
        	elementData[size++] = e;
        	return true;
    	}
    	//add方法又调用了ensureCapacityInternal()方法
    	private void ensureCapacityInternal(int minCapacity) {
        	if (elementData == EMPTY_ELEMENTDATA) {
            		minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        	}
        	ensureExplicitCapacity(minCapacity);
    	}
    	//ensureCapacityInternal()又调用了ensureExplicitCapacity()方法
    	private void ensureExplicitCapacity(int minCapacity) {
	        modCount++;
	        if (minCapacity - elementData.length > 0)
	            grow(minCapacity);
	 }
    	//Itr内部类
	private class Itr implements Iterator<E> {
		int expectedModCount = modCount;
	    //checkForComodification()方法
		final void checkForComodification() {
	            if (modCount != expectedModCount)
	                throw new ConcurrentModificationException();
	     }
	     @SuppressWarnings("unchecked")
	     //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];
	        }
	}
}

解释:ArrayList实现了List接口,要实现List中的Iterator方法和add方法,所以用Iterator方法进行迭代时,我们要用ArrayList类中重写的Iterator()方法进行迭代。进入Iterator()方法,它返回了一个Itr内部类的对象return new Itr();,接下来,我们来到Itr内部类,根据异常的信息提示,是Itr内部类的next()出了问题,来到next()方法,第一句代码便是调用了方法checkForComodification();根据异常提示,也确实是这个方法最终导致了并发修改异常,我们来到该方法,代码如下:

if (modCount != expectedModCount)
                 throw new ConcurrentModificationException();

如果modCount不等于expectedModCount,则抛出并发修改异常。
首先我们需要知道这两个值什么意思:
modCount:实际修改次数
expectedModCount:预期修改次数
  我们来到Itr内部类,该类第一句代码就为int expectedModCount = modCount;所以一开始实际修改次数和预期修改次数是相等的,而modCount来自于ArrayList的父类AbstractList,在AbstractList中,我们可以找到这样一句代码protected transient int modCount = 0;。所以expectedModCount 和modCount初始值都为0,
  来到main方法,当我们调用hasNext()方法后再调用next()方法后,一切问题都没有,但是当我们进行equals判断为true调用add()方法后,就有问题了,进入add()方法,开始一句是调用了ensureCapacityInternal()方法,这个方法又调用了ensureExplicitCapacity()方法,在这里我们看见了一句modCount++;也就是说当执行完一次add()方法后modCount变化了,但是expectedModCount却没有变化,所以当再次调用next(),next()又调用checkForComodification()方法后就会发现modCount !=expectedModCount,然后抛出异常。

3.解决方法

使用for循环:

public class Demo {
 	public static void main(String[] args) {
  		ArrayList<String> list = new ArrayList<String>();
  		list.add("I");
  		list.add("love");
  		list.add("java");
  		for(int i=0;i<list.size();i++){
   			String s=list.get(i);
   			if(s.equals("love")){
    				list.add("very much");
   			}
  		}
  		System.out.println(list);
 	}
}

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值