java.util.ConcurrentModificationException:并发修改异常以ArrayList的迭代器举例

并发修改异常,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错误。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值