为什么抛出ConcurrentModificationException以及如何调试它

为什么抛出ConcurrentModificationException以及如何调试它

我用的是Collection(A)HashMapJPA间接使用,但显然代码会随机抛出一个ConcurrentModificationException..是什么导致了这一问题,我如何解决这个问题?也许是通过一些同步?

下面是完整的堆栈跟踪:

Exception in thread "pool-1-thread-1" java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
        at java.util.HashMap$ValueIterator.next(Unknown Source)
        at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:555)
        at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
        at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
        at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
        at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
        at org.hibernate.engine.Cascade.cascade(Cascade.java:130)


慕标琳琳
浏览 1657回答 3
3回答

隔江千里

这不是同步问题。如果正在迭代的基础集合被Iterator本身以外的任何东西修改,就会发生这种情况。Iterator it = map.entrySet().iterator();while (it.hasNext()){    Entry item = it.next();    map.remove(item.getKey());}这将在第二次调用它.hasNext()时抛出一个ConcurrentModificationException。正确的方法是   Iterator it = map.entrySet().iterator();    while (it.hasNext())    {       Entry item = it.next();       it.remove();    }假设此迭代器支持Remove()操作。

慕桂英3389331

尝试使用ConcurrentHashMap而不是普通的HashMap

侃侃无极

修改aCollection当迭代的时候Collection使用Iterator是不允许大部分Collection上课。Java库调用试图修改Collection虽然迭代它是一个“并发修改”,但不幸的是,这表明唯一可能的原因是由多个线程同时进行修改,但事实并非如此。只使用一个线程,就可以为Collection(使用Collection.iterator(),或增强型for环路),开始迭代(使用Iterator.next(),或等效地进入增强体的主体for循环),修改Collection,然后继续迭代。为了帮助程序员,一些的实现Collection班尝试若要检测错误的并发修改,请抛出ConcurrentModificationException如果他们发现了。但是,保证检测到所有并发的修改通常是不可能的,也是不可行的。如此错误地使用Collection并不总是导致抛出ConcurrentModificationException..的文件ConcurrentModificationException说:此异常可能由检测到对象的并发修改的方法引发,而这种修改是不允许的.请注意,此异常并不总是指示对象已被不同线程并发修改。如果单个线程发出一系列违反对象契约的方法调用,该对象可能会抛出此异常.请注意,通常情况下,不能保证快速失败的行为在存在不同步并发修改的情况下提供任何硬的保证。失败操作抛出ConcurrentModificationException在尽最大努力的基础上。请注意例外可以,可能被扔,而不是扔必被抛出不需要不同的线程。无法保证抛出异常抛出异常是在尽力而为的基础上进行的。抛出异常会发生当并发修改是检出,不是当它是引起的文件HashSet, HashMap, TreeSet和ArrayList课程上说:[直接或间接地从该类返回的迭代器]失败快速:如果[集合]在迭代器创建后的任何时候被修改,则除通过迭代器自身的Remove方法外,以任何方式修改Iterator抛出ConcurrentModificationException..因此,在并发修改的情况下,迭代器会迅速而干净地失败,而不是在未来某个未定的时间冒着任意的、不确定的行为的风险。请注意,迭代器的快速失败行为不能保证,一般来说,在不同步并发修改的情况下不可能提供任何硬的保证。失败快速迭代器抛出ConcurrentModificationException在尽最大努力的基础上。因此,编写一个依赖于此异常的程序是错误的:迭代器的抗故障行为应该仅用于检测bug。.请再次注意,这种行为“不能保证”,而且仅仅是“在尽最大努力的基础上”。的几种方法的文档化。Map接口说明如下:非并发实现应该覆盖此方法,并在最大努力的基础上抛出ConcurrentModificationException如果检测到映射函数在计算期间修改此映射。并发实现应该覆盖此方法,并在最大努力的基础上抛出IllegalStateException如果检测到映射函数在计算期间修改此映射,则计算将永远不会完成。请再次注意,检测只需要“尽最大努力的基础”,并且ConcurrentModificationException显式地建议只用于非并发(非线程安全)类。调试ConcurrentModificationException因此,当您看到堆栈跟踪是由于ConcurrentModificationException,您不能立即假定原因是不安全的多线程访问Collection..你必须检查堆栈跟踪决定哪一类Collection抛出异常(类的方法将直接或间接抛出它),其中Collection对象。然后,您必须从可以修改对象的位置进行检查。最常见的原因是修改Collection在增强的范围内for循环在Collection..仅仅因为你没有看到Iterator对象在您的源代码中并不意味着没有Iterator那里!幸运的是,有一个错误的说法for循环通常在堆栈跟踪中,因此跟踪错误通常很容易。更棘手的情况是,当您的代码在引用中传递到Collection对象。请注意不可修改收藏的视图(如由Collections.unmodifiableList())保留对可修改集合的引用,因此对“不可修改”集合的迭代可以引发异常。(修改已在其他地方完成)。其他意见你的Collection,如子列表, Map条目集和Map密钥集还保留对原始(可修改)的引用Collection..这可能是一个问题,即使是线程安全。Collection,如CopyOnWriteList不要假设线程安全(并发)集合永远不会抛出异常。哪些操作可以修改Collection在某些情况下可能是意外的。例如,LinkedHashMap.get()修改其集合.最困难的情况是例外是由多个线程并发修改造成的。防止并发修改错误的编程如果可能,将所有引用限制在Collection对象,因此它更容易防止并发修改。使.Collection a private对象或局部变量,并且不返回对Collection或者方法中的迭代器。这样就容易得多了。全在那里Collection可以修改。如果Collection将由多个线程使用,则确保线程访问Collection只有适当的同步和锁定。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java