Why is a ConcurrentModificationException thrown and how to debug it
我使用的是JPA间接使用的
以下是完整的堆栈跟踪:
1 2 3 4 5 6 7 8 9 | 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) |
这不是同步问题。如果正在迭代的基础集合被除迭代器本身之外的任何东西修改,则会发生这种情况。
1 2 3 4 5 6 | Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Entry item = it.next(); map.remove(item.getKey()); } |
这将在第二次调用it.hasNext()时引发ConcurrentModificationException。
正确的方法是
1 2 3 4 5 6 | Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Entry item = it.next(); it.remove(); } |
假设此迭代器支持remove()操作。
尝试使用Concurrenthashmap而不是普通的hashmap
用
为了帮助编程人员,这些
The documentation of
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible...
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception...
Note that fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw
ConcurrentModificationException on a best-effort basis.
注
- 例外可能被击中,而不一定被击中。
- 不需要不同的螺纹
- Throwing the exception cannot be guaranteed
- 推进例外是一个最好的基础
- Throwing the exceptions happens when the contral modification is detected,not when it's caused
1.The documentation of the
BLCK1/
Note again that the behaviour"cannot be guaranteed"and is only"on a best-effort basis".
The documentation of several methods of the EDOCX1&20)Interface say this:
Non-concurrent implementations should override this method and, on a best-effort basis, throw a
ConcurrentModificationException if it is detected that the mapping function modifies this map during computation. Concurrent implementations should override this method and, on a best-effort basis, throw anIllegalStateException if it is detected that the mapping function modifies this map during computation and as a result computation would never complete.
又注意到只有一种"最佳努力基础"是检测所必需的,而一种
EDOCX1&12
所以,当你看到一个由
- 最常见的原因是
Collection 的改变,在for 上环绕着Collection 。因为你看不到源代码中的一个对象不代表没有偶然地,一个故障的说明,EDOCX1&7)LOOP通常会出现在堆栈的轨迹中,因此,错误的追踪通常是容易的。 - 一个骗子的例子是当你的代码绕着
Collection 号对象传递时。Note that unmodificable views of collections(such as produced by EDOCX1&34).Retain a reference to the modificable collection,so iteration over an"unmodificable"collection can throw the exception(the modification has been elsewhere).Other views of yourCollection ,sub lists,Map entry sets andMap key sets also retain references to the original(remodible)Collection 这可能是一个问题,甚至是一个安全的Collection ,例如CopyOnWriteList ;不要认为安全的(竞争者)集群永远不会被排除在外。 - 在某些情况下,可不期望操作的
Collection 。修改它的收藏。 - 硬案例是由于多径并行修改而引起的例外情况。
防止并行修正错误的编程
在可能的情况下,将所有的参考资料都限定在一个
这听起来不像Java同步问题,更像是数据库锁定问题。
我不知道向所有持久类添加一个版本是否会解决问题,但这是Hibernate提供对表中行的独占访问的一种方法。
可能是隔离级别需要更高。如果您允许"脏读",也许您需要进行串行化。
Note that the selected answer cannot be applied to your context directly before some modification, if you are trying to remove some entries from the map while iterating the map just like me.
我只是举个例子,让新手节省时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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(); } |
根据您尝试执行的操作,尝试CopyOnWriteArrayList或CopyOnWriteArraySet。