zoukankan      html  css  js  c++  java
  • 记一次偶然的java.util.ConcurrentModificationException异常

    ArrayList集合迭代器删除报错:java.util.ConcurrentModificationException

        public static void main(String[] args) {
    List<String> arrayList = new ArrayList<>(); arrayList.add("1"); arrayList.add("2"); arrayList.add("3"); arrayList.add("4"); new Thread(()-> { try { Iterator<String> iterator = arrayList.iterator(); while (iterator.hasNext()) { String curString = iterator.next();
    if("2".equals(curString)){ iterator.remove(); //java.util.ConcurrentModificationException } } System.out.println(Thread.currentThread() + ":" + arrayList); } catch (Exception e) { e.printStackTrace(); } }).start(); Iterator<String> iterator = arrayList.iterator(); while (iterator.hasNext()) { String curString = iterator.next(); if("2".equals(curString)){ iterator.remove(); } } System.out.println(arrayList);

    运行结果可能报错:

    java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
        at java.util.ArrayList$Itr.remove(ArrayList.java:865)

    查看源码:java.util.ArrayList.Itr,定位报错代码。865行调用checkForComodification()检查并发修改。

    再进入checkForComodifacation(),901行 throw new ConcurrentModificationException();

    找到原因:当modCount != expectedModCount 时,就会报ConcurrentModificationException异常。

    modCount 是抽象类AbstractList的属性,ArrayList继承AbstractList,ArrayList的add和remove方法会使modCount++。

    expectedModCount是迭代器Itr类的属性,初始值等于modCount ,用迭代器删除元素后会使expectedModCount=modCount 。

     所以,多个线程用迭代器方式遍历同一个集合删除元素时,modCount是共享变量,当一个线程删除元素后modCount++了,导致另一个线程迭代器Itr的expectedModCount比modCount小,调用checkForComodification()时,modCount != expectedModCount ,就会报ConcurrentModificationException异常。

    解决方案:

    1. 加锁,用反向for循环遍历来删除元素

      public static void main(String[] args) {
                List<String> arrayList = new ArrayList<>();
                arrayList.add("1");
                arrayList.add("2");
                arrayList.add("3");
                arrayList.add("4");
                ReentrantLock reentrantLock = new ReentrantLock();//可重入锁
                new Thread(()-> {
                    try {
                        reentrantLock.lock();
                     for (int i = arrayList.size() -1; i >= 0; i--) {
                          String curString = arrayList.get(i);
                          if("2".equals(curString)){
                              arrayList.remove(i);
                         }
                      }
                      System.out.println(Thread.currentThread() + ":" + arrayList);
                     reentrantLock.unlock();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
           //mian线程 reentrantLock.lock();
    for (int i = arrayList.size() -1; i >= 0; i--) { String curString = arrayList.get(i); if("2".equals(curString)){ Thread.sleep(1000l); arrayList.remove(i); } } reentrantLock.unlock(); System.out.println(arrayList); }

    运行结果:由于有锁,所以不会删错元素

     

    2. 使用CopyOnWriteArrayList类代替ArrayList,注意:CopyOnWriteArrayList不支持迭代器删除。

    public static void main(String[] args) throws InterruptedException {
                List<String> arrayList = new ArrayList<>();
                arrayList.add("1");
                arrayList.add("2");
                arrayList.add("3");
                arrayList.add("4");
                CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>(arrayList);
                new Thread(()-> {
                    try {
                        for(String string : copyOnWriteArrayList){
                            if("2".equals(string)){
                                copyOnWriteArrayList.remove(string);
                            }
                        }
                        System.out.println(Thread.currentThread() + ":" + copyOnWriteArrayList);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
                //mian线程
                for(String string : copyOnWriteArrayList){
                    if("2".equals(string)){
                        copyOnWriteArrayList.remove(string);
                    }
                }
                System.out.println(copyOnWriteArrayList);
    }

    运行结果:CopyOnWriteArrayList写操作都加了ReentrantLock锁,写的时候会新增一个数组,用复制数组的方式写,读写分离。

    CopyOnWriteArrayList不保证数据实时一致性,只保证最终一致性。适合读多写少的场景,因为写的时候内存会占用两个数组。

    好记性不如烂笔头,提升自己:http://www.urlort.cn/1DjfQb github地址:https://github.com/997480972
  • 相关阅读:
    Elasticsearch入门教程
    Elasticsearch环境安装配置
    Elasticsearch教程
    linux下elasticsearch 安装、配置及示例
    Elasticsearch集群部署
    elasticsearch-hadoop使用
    Oracle监控的关键指标
    oracle分区表的使用和查询
    nginx访问控制用户认证两种方式
    nginx创建默认虚拟主机
  • 原文地址:https://www.cnblogs.com/liuyong1993/p/10444513.html
Copyright © 2011-2022 走看看