问题
I have implemented Resteasy service which is working under Apache Tomcat. It works fine, but sometimes, when sending request to service, very rarely I receive such crazy error:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.sun.xml.bind.v2.runtime.reflect.Lister$CollectionLister$1.next(Lister.java:288)
at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:139)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:159)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:358)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:69)
at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:172)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:159)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:358)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:593)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:251)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(Unknown Source)
at org.jboss.resteasy.plugins.providers.jaxb.AbstractJAXBProvider.writeTo(AbstractJAXBProvider.java:138)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:129)
at org.jboss.resteasy.core.interception.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:62)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:118)
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor.aroundWriteTo(GZIPEncodingInterceptor.java:100)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:122)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:99)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:411)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:376)
at org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:217)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:224)
at org.jboss.resteasy.plugins.server.servlet.FilterDispatcher.doFilter(FilterDispatcher.java:62)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Are any ideas about reason for such error or ways, how to reproduce/fix it?
UPDATE
There are several answers where you, guys, suggest me to implement Synchronized
collections inside my beans. It is not a way, because:
1) I generate my beans from XSD using standard generators. I have approx. 100 different beans and I do not want to change them all manually
2) There is not any mention about need of synchonized collections neither in JAXB
docs, neither in RESTEasy
docs.
回答1:
It seems that Resteasy is in the process of generating an XML response, but the object tree that was passed to the JAXB marshaller has been modified by another thread in the application.
A list in the marshalled object tree has been modified by another thread, and so when the marshaller goes through the list, it causes a ConcurrentModificationException.
Is it normal that there are two application threads accessing the same bean, are there some beans stored in static variables for example ?
Try turning on -Djaxb.debug=true to get further info on which bean property it's causing the problem. It might not always be the same property causing the error.
Another way of identifying the property is putting a breakpoint in the line that throws the CME, and checking what is the type of the elements of the list is.
If the beans in the application are intended to be accessed by multiple threads, then it's necessary to make the bean classes thread safe but based on what you mention there is no evident shared state - so your beans should only be accessed by one thread at a time.
You can always take a few heap dumps and search in eclipse memory analyzer for which threads are referencing objects of the list/lists causing problems.
回答2:
Is the ArrayList being created as a synchronized collection? You may want to use this approach to avoid concurrency issues. Note the synchronized recommendation while iterating:
http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedCollection(java.util.Collection)
public static <T> Collection<T> synchronizedCollection(Collection<T> c)
Returns a synchronized (thread-safe) collection backed by the specified collection. In order to guarantee serial access, it is critical that all access to the backing collection is accomplished through the returned collection.
It is imperative that the user manually synchronize on the returned collection when iterating over it:
Collection c = Collections.synchronizedCollection(myCollection);
...
synchronized (c) {
Iterator i = c.iterator(); // Must be in the synchronized block
while (i.hasNext())
foo(i.next());
}
Failure to follow this advice may result in non-deterministic behavior.
The returned collection does not pass the hashCode and equals operations through to the backing collection, but relies on Object's equals and hashCode methods. This is necessary to preserve the contracts of these operations in the case that the backing collection is a set or a list.
The returned collection will be serializable if the specified collection is serializable.
回答3:
Your bean's ArrayList
property is not supporting concurrency.
If you check the source code (here):
public static final class CollectionLister<BeanT,T extends Collection> extends Lister<BeanT,T,Object,T> {
public ListIterator iterator(T collection, XMLSerializer context) {
final Iterator itr = collection.iterator();
return new ListIterator() {
public boolean hasNext() {
return itr.hasNext();
}
public Object next() {
return itr.next();
}
};
}
}
You will notice the line throw error is Collection.iterator.next()
. Which mean that the list is changed while JAXB is loop through the list property.
To solve this, try to use Collections.synchronizedList(List list) to create synchronized list.
Example:
public class Bean {
private List<String> phones = Collections.synchronizedList(new ArrayList<>());
public void setPhones(List<String> phones) {
if(phones == null) {
this.phones = Collections.synchronizedList(new ArrayList<>());
} else {
this.phones = Collections.synchronizedList(phones);
}
}
}
来源:https://stackoverflow.com/questions/20300792/rare-concurrentmodificationexception-in-jboss-resteasy-service