我用一个 Collection (a HashMap 使用间接地通过访问用户,它如此发生),但显然随机的代码扔一个 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)
有帮助吗?

解决方案

这不是同步问题。如果正在迭代的基础集合被Iterator本身以外的任何东西修改,则会发生这种情况。

Iterator it = map.entrySet().iterator();
while (it.hasNext())
{
   Entry item = it.next();
   map.remove(item.getKey());
}

第二次调用it.hasNext()时会抛出ConcurrentModificationException。

正确的方法是

   Iterator it = map.entrySet().iterator();
   while (it.hasNext())
   {
      Entry item = it.next();
      it.remove();
   }

假设这个迭代器支持remove()操作。

其他提示

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

修改的一个 Collection 虽然循环, Collection 使用 Iterator不允许 通过最多的 Collection 课程。Java图书馆的电话,试图修改一个 Collection 虽然循环,通过这个"并行的修改",这不幸表明的唯一可能的原因是同时进行的修改通过多线程,但这是不是这样。只用一个线程能够创建一个迭代的 Collection (使用 Collection.iterator(), 或 增强 for 循环),开始循环(使用 Iterator.next(), 或者等效地进入身体的增强的 for 环),修改 Collection, 然后继续迭代.

要帮助的程序员, 一些 实现的那些 Collection尝试 来检测错误的并行修改,并扔掉 ConcurrentModificationException 如果他们检测到它。然而,一般不可能和实际保障检测的所有并行的修改。这样错误的使用 Collection 并不总是导致在一扔 ConcurrentModificationException.

该文件 ConcurrentModificationException 说:

这一例外可能引发的方法检测的并行修改的对象时这样的修改是不允许的...

注意,这种异常不总是表明,对象同时修改通过不同的线。如果一个单一的线问题的顺序的方法调用,违反合同的对象,对象可以把这个异常...

注意,快速失败的行为不能得到保证,一般来说,不可能作出任何坚决的保证的存在不同步并行的修改。快速失败的行动扔 ConcurrentModificationException 在最好的办法了基础。

注意,

该文件 HashSet, HashMap, TreeSetArrayList 课程这样说:

迭代器返回的[直接或间接地从这类]是的快速失败:如果[收集]修改后的任何时间的迭代创建的,以任何方式除了通过迭代的自己的移除的方法, Iterator 抛出了一个 ConcurrentModificationException.因此,在面的并行修改,迭代很快就会完全失败,而不是冒险的任意的、不确定行为的不确定的时间中的未来。

注意,快速失败行为的迭代,不能保证,一般来说,不可能作出任何坚决的保证的存在不同步并行的修改。快速失败的迭代扔 ConcurrentModificationException 在最好的办法了基础。因此,它将是错误的编写程序,取决于这种异常的正确性: 快速失败行为的迭代,应仅用于检测的错误.

再次注意的行为"无法予以保证"并且是唯一的"最佳努力的基础"。

该文件的几个方法 Map 接口,这样说:

非并实现应该重写这个方法并在一个最佳努力的基础上,扔一个 ConcurrentModificationException 如果检测到的映射功能修改这张地图在计算。并行的实施应当复盖这种方法并在一个最佳努力的基础上,扔一个 IllegalStateException 如果检测到的映射功能修改这种地图时运算和结果的计算将永远不会完成。

再次注意,只有一个"最佳努力的基础上"需要的检测,以及一个 ConcurrentModificationException 是明确建议只有为无并发(无线安全)课程。

调试 ConcurrentModificationException

所以,当你看到一堆追踪由于一个 ConcurrentModificationException, 你不能立即承担,原因是不安全的多线程访问 Collection.你必须 审查栈-跟踪 确定哪些类的 Collection 扔的例外(a类方法将有直接或间接扔),并为 Collection 对象。然后你必须审查从那里的对象可以修改。

  • 最常见的原因是修改 Collection 在一种增强的 for 循环 Collection.只是因为你没有看到一个 Iterator 目的在你的源代码并不意味着没有 Iterator 那里!幸运的是,一个声明的问题 for 循环通常会在栈-跟踪,因此追踪下的错误通常是容易的。
  • 一个棘手的情况是当你的代码通过围绕引用的 Collection 对象。注意, 不可修改的 风景的集合(例如生产 Collections.unmodifiableList())保留参照可改变的集合,所以 迭代过一个"不可修改的"收集可以扔的例外 (该修改已经完成在其他地方).其他的 的风景 你的 Collection, 如 子名单, Map 进入集Map 关键设定 还保留提及原始(修改) Collection.这可能是一个问题,即使一线的安全 Collection, 如 CopyOnWriteList;不要假定线安全,(并)胶凝素可以永远不会扔的例外。
  • 其行动可以修改 Collection 可以意想不到在一些情况。例如, LinkedHashMap.get() 修改其收藏.
  • 最难的情况下正当的例外 由于并行的修改通过多线程。

编程,以防止并行修改的错误。

在可能时限制所有提到一个 Collection 对象,使其更容易,以防止并行修改。让 Collection 一个 private 目或局部变量,而不返回的引用 Collection 或其从迭代方法。它是那么容易得多,检查 所有 的地方 Collection 可以修改。如果的 Collection 是要被使用过多的线,然后实践,以确保线访问 Collection 只有适当同步,并锁定。

这听起来不像Java同步问题,更像是数据库锁定问题。

我不知道在所有持久化类中添加一个版本是否会将其排序,但这是Hibernate可以提供对表中行的独占访问的一种方式。

可能是隔离级别需要更高。如果您允许“脏读”,则可能需要进行序列化。

根据您的尝试,尝试CopyOnWriteArrayList或CopyOnWriteArraySet。

  

请注意,如果您在尝试像我一样迭代地图时从地图中删除某些条目,则在进行某些修改之前,所选答案无法直接应用于您的上下文。

我只是在这里为新手提供我的工作示例,以节省他们的时间:

HashMap<Character,Integer> map=new HashMap();
//adding some entries to the map
...
int threshold;
//initialize the threshold
...
Iterator it=map.entrySet().iterator();
while(it.hasNext()){
    Map.Entry<Character,Integer> item=(Map.Entry<Character,Integer>)it.next();
    //it.remove() will delete the item from the map
    if((Integer)item.getValue()<threshold){
        it.remove();
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top