ConcurrentModificationException 的根源分析

本文深入解析了ConcurrentModificationException异常的产生原因,并提供了避免该异常的解决方案。通过代码示例展示了单线程环境下如何触发这一异常,同时探讨了集合操作中的隐式迭代问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1107304683 0 0 159 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} @font-face {font-family:"Comic Sans MS"; panose-1:3 15 7 2 3 3 2 2 2 4; mso-font-charset:0; mso-generic-font-family:script; mso-font-pitch:variable; mso-font-signature:647 0 0 0 159 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-font-kerning:1.0pt;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; mso-ascii-font-family:Calibri; mso-fareast-font-family:宋体; mso-hansi-font-family:Calibri; mso-font-kerning:0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:841.9pt 595.3pt; mso-page-orientation:landscape; margin:1.0cm 1.0cm 1.0cm 1.0cm; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} -->

并发修改异常是一个在系统中很常见的异常,但是很多开发人员对这个异常有误解,一定认为是在并发修改时修改产生该异常,什么是并发修改了,就是在多线程中修改,但是在检查自己带码时候并没有使用到多线程,所以就一直找不到问题根源,根源请参见下面。。。。

 

ConcurrentModificationException 的解释

      我们可以查看 JDKAPI 来参考该异常的解释 当方法检测到对象的并发修改时,抛出此异常。

 

我门系统中大部分在是在使用 Collection 上进行迭代时抛出异常,但是我们系统都是使用的单线程,为什么会发生该异常了

我们参考下面错误代码,一个简单的需求,用户获取一个集合数据,需要讲不满足用户需求的数据删除掉

 

代码:

// 从业务方法中查询数据

           List<String> list = service .queryResult();

           for (String value : list)

           {

                 //bbb 数据不是用户需要的数据,我们需要移除

                 if (value.equals( "aaa" ))

                 {

                       // 将不满足用户数据删除

                      list.remove(value);

                 }

           }

  运行结果 :

       Exception in thread "main" java.util.ConcurrentModificationException

                at java.util.AbstractList$Itr.checkForComodification( AbstractList.java:372 )

                at java.util.AbstractList$Itr.next( AbstractList.java:343 )

                at com.demo.ConcurrentExceptionDemo.main( ConcurrentExceptionDemo.java:24 )

我们可以发现上面的代码并没有什么并发问题,到底是什么原因发生该异常,我们可以再次查看 API 文档

  引用文档上面的话:“ 此异常不会始终指出对象已经由不同 线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常”现在我们知道了单线程也是会抛出该异常的,由于我们违反了对象的协定的方法调用序列,但是我们是在哪里违反了协定了?我们再次查看 API 文档上面发现 : “行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险”,这句话什么意思了?看了很久没有搞明白,所以直接查看源代码发现:、

 

final void checkForComodification () {

          if ( modCount != expectedModCount )

           throw new ConcurrentModificationException();

      }

}

我们使用的迭代器在调用 next ()调用了改方法 checkForComodification () ,当每次检查 modCount != expectedModCount 就会抛出该异常,

下面我们来再次分析我们的代码

modCount 是集合每次在调用 remove add 方法时都会记录修改次数 modCount++ 但是我们迭代产生后 expectedModCount 就是一个固定值了,所以你只要在迭代时修改了集合就会造成 modCount != expectedModCount ,所以就抛出了 ConcurrentModificationException

 

现在我们应该已经搞清楚了改异常产生的原因了,但是我们还是要实现上面的需求,不满足要求还是要删除,我们代码该怎么实现了

方案一 : 通过调用迭代器的 remove 方法来实现删除

Iterator<String> ite = list.iterator();

           while (ite.hasNext())

           {

                 //aaa 数据不是用户需要的数据,我们需要移除

                 if (ite.next().equals( "aaa" ))

                 {

                      list.remove( "aaa" );

                 }

           }

我们来看看为什么调用迭代器的 remove 方法不会发生该异常了,下面是该迭代器的 remove 方法源码

public void remove() {

          if ( lastRet == -1)

           throw new IllegalStateException ();

            checkForComodification();

          try {

           AbstractList. this .remove( lastRet );

           if ( lastRet < cursor )

               cursor --;

           lastRet = -1;

           expectedModCount = modCount;

          } catch (IndexOutOfBoundsException e) {

           throw new ConcurrentModificationException();

          }

      }

我们发现上面加粗标红的字体就应该明白原因了吧。

方案二 : 首先获取需要删除的元素,然后通过迭代要删除的元素来删除

           List<String> delResults = new ArrayList<String>();

           for (String value : list)

           {

                 //bbb 数据不是用户需要的数据,我们需要移除

                 if (value.equals( "bbb" ))

                 {

                      delResults.add(value);

                 }

           }

           // 然后迭代要删除的集合来删除

           for (String delValue : delResults)

           {

                 list.remove(delValue);

           }

这样也是没有问题的,但是我们发现方案二好像比方法一多做了线性循环。

 

 

集合抛出 ConcurrentModificationException 进阶

我们下面来看一下下面代码

package com.demo;

import java.util.HashSet;

import java.util.Random;

import java.util.Set;

import java.util.concurrent.SynchronousQueue;

public class HiddenIterator

{

    private Set<Integer> set = new HashSet<Integer>();

    public void add(Integer i)

    {

        set.add(i);

    }

    public void remove(Integer i)

    {

        set.remove(i);

    }

    public void addTenThings()

    {

        Random rand = new Random();

        for (int i =0;i<10;i++)

        {

            add(rand.nextInt());

            System.out.println("DEBUG add ten elements to set : " + set);

        }

    }

    public static void main(String []args)

    {

        final HiddenIterator hi = new HiddenIterator();

        new Thread(){

            public void run() {

                hi.addTenThings();

            };

        }.start();

        hi.addTenThings();

    }

}

 

我们来看看改代码,暂时不要管上面标红的行 ,就是在添加了一个元素打印集合中的值 ,我们看看程序的原意就是通过两个线程来向集合中添加元素,运行该代码发现下面结果

Exception in thread "Thread-0" java.util.ConcurrentModificationException

      at java.util.HashMap$HashIterator.nextEntry( HashMap.java:793 )

      at java.util.HashMap$KeyIterator.next( HashMap.java:828 )

      at java.util.AbstractCollection.toString( AbstractCollection.java:421 )

      at java.lang.String.valueOf( String.java:2826 )

      at java.lang.StringBuilder.append( StringBuilder.java:115 )

      at com.demo.HiddenIterator.addTenThings( HiddenIterator.java:28 )

      at com.demo.HiddenIterator$1.run( HiddenIterator.java:37 )

 

我们上面已经知道集合是在做迭代集合的时候才会发生该异常,我们可以看集合的 add() 方法里也完全没有抛出该异常的代码。这就百思不得其解了,然后一句一句的代码查看,原来发现问题原因处在了我们上面标红的代码行。我们其实只要注释掉打印语句就不会发生该异常了

原来我们打印集合时,会自动调用 toString ()方法,我们现在查看 toString ()方法源码

public String toString () {

        Iterator<E> i = iterator();

      if (! i.hasNext())

          return "[]" ;

 

      StringBuilder sb = new StringBuilder();

      sb.append( '[' );

      for (;;) {

          E e = i.next();

          sb.append(e == this ? "(this Collection)" : e);

           if (! i.hasNext())

           return sb.append( ']' ).toString();

          sb.append( ", " );

      }

原来在调用集合 toString 方法时做了集合的迭代操作,所以导致了改异常的产生,因此我们在使用集合时要注意集合的一些隐式迭代操作,整理了一下集合的 hashCode,equals,toString,containsAll,removeAll,retainAll 方法都做了隐式迭代,所以我们在并发环境下将集合作为另一个容器的元素和一个容易的 key 时要注意,以及把容器作为参数的构造函数时都会对容器进行迭代,因此在这些操作下都可能会引起 ConcurrentModificationException

 

 

      

标题基于SpringBoot+Vue的学生交流互助平台研究AI更换标题第1章引言介绍学生交流互助平台的研究背景、意义、现状、方法与创新点。1.1研究背景与意义分析学生交流互助平台在当前教育环境下的需求及其重要性。1.2国内外研究现状综述国内外在学生交流互助平台方面的研究进展与实践应用。1.3研究方法与创新点概述本研究采用的方法论、技术路线及预期的创新成果。第2章相关理论阐述SpringBoot与Vue框架的理论基础及在学生交流互助平台中的应用。2.1SpringBoot框架概述介绍SpringBoot框架的核心思想、特点及优势。2.2Vue框架概述阐述Vue框架的基本原理、组件化开发思想及与前端的交互机制。2.3SpringBoot与Vue的整合应用探讨SpringBoot与Vue在学生交流互助平台中的整合方式及优势。第3章平台需求分析深入分析学生交流互助平台的功能需求、非功能需求及用户体验要求。3.1功能需求分析详细阐述平台的各项功能需求,如用户管理、信息交流、互助学习等。3.2非功能需求分析对平台的性能、安全性、可扩展性等非功能需求进行分析。3.3用户体验要求从用户角度出发,提出平台在易用性、美观性等方面的要求。第4章平台设计与实现具体描述学生交流互助平台的架构设计、功能实现及前后端交互细节。4.1平台架构设计给出平台的整体架构设计,包括前后端分离、微服务架构等思想的应用。4.2功能模块实现详细阐述各个功能模块的实现过程,如用户登录注册、信息发布与查看、在线交流等。4.3前后端交互细节介绍前后端数据交互的方式、接口设计及数据传输过程中的安全问题。第5章平台测试与优化对平台进行全面的测试,发现并解决潜在问题,同时进行优化以提高性能。5.1测试环境与方案介绍测试环境的搭建及所采用的测试方案,包括单元测试、集成测试等。5.2测试结果分析对测试结果进行详细分析,找出问题的根源
内容概要:本文详细介绍了一个基于灰狼优化算法(GWO)优化的卷积双向长短期记忆神经网络(CNN-BiLSTM)融合注意力机制的多变量多步时间序列预测项目。该项目旨在解决传统时序预测方法难以捕捉非线性、复杂时序依赖关系的问题,通过融合CNN的空间特征提取、BiLSTM的时序建模能力及注意力机制的动态权重调节能力,实现对多变量多步时间序列的精准预测。项目不仅涵盖了数据预处理、模型构建与训练、性能评估,还包括了GUI界面的设计与实现。此外,文章还讨论了模型的部署、应用领域及其未来改进方向。 适合人群:具备一定编程基础,特别是对深度学习、时间序列预测及优化算法有一定了解的研发人员和数据科学家。 使用场景及目标:①用于智能电网负荷预测、金融市场多资产价格预测、环境气象多参数预报、智能制造设备状态监测与预测维护、交通流量预测与智慧交通管理、医疗健康多指标预测等领域;②提升多变量多步时间序列预测精度,优化资源调度和风险管控;③实现自动化超参数优化,降低人工调参成本,提高模型训练效率;④增强模型对复杂时序数据特征的学习能力,促进智能决策支持应用。 阅读建议:此资源不仅提供了详细的代码实现和模型架构解析,还深入探讨了模型优化和实际应用中的挑战与解决方案。因此,在学习过程中,建议结合理论与实践,逐步理解各个模块的功能和实现细节,并尝试在自己的项目中应用这些技术和方法。同时,注意数据预处理的重要性,合理设置模型参数与网络结构,控制多步预测误差传播,防范过拟合,规划计算资源与训练时间,关注模型的可解释性和透明度,以及持续更新与迭代模型,以适应数据分布的变化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值