Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
JAXBContext
is thread safe and should only be created once and reused to avoid the cost of initializing the metadata multiple times. Marshaller
and Unmarshaller
are not thread safe, but are lightweight to create and could be created per operation.
Ideally, you should have a singleton JAXBContext
and local instances of Marshaller
and Unmarshaller
.
JAXBContext
instances are thread-safe while Marshaller
and Unmarshaller
instances are not thread-safe and should never be shared across threads.
It's a pity that this isn't specifically described in the javadoc. What I can tell is that Spring uses a global JAXBContext, shared between threads, whereas it creates a new marshaller for each marshalling operation, with a javadoc comment in the code saying that JAXB marshallers are not necessarily thread-safe.
The same is said on this page:https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety.
I would guess that creating a JAXBContext is a costly operation, because it involves scanning classes and packages for annotations. But measuring it is the best way to know.
JAXB 2.2 (JSR-222) has this to say, in section "4.2 JAXBContext":
To avoid the overhead involved in creating a
JAXBContext
instance, a JAXB application is encouraged to reuse a JAXBContext instance. An implementation of abstract class JAXBContext is required to be thread-safe, thus, multiple threads in an application can share the same JAXBContext instance.[..]
JAXBContext class is designed to be immutable and thus threadsafe. Given the amount of dynamic processing that potentially could take place when creating a new instance of JAXBContxt, it is recommended that a JAXBContext instance be shared across threads and reused as much as possible to improve application performance.
Unfortunately, the specification does not make any claims regarding thread-safety of Unmarshaller
and Marshaller
. So it is best to assume they are not.
I solved this problem using:
public class MyClassConstructor {
private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<Unmarshaller>() {
protected synchronized Unmarshaller initialValue() {
try {
return jaxbContext.createUnmarshaller();
} catch (JAXBException e) {
throw new IllegalStateException("Unable to create unmarshaller");
}
}
};
private final ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<Marshaller>() {
protected synchronized Marshaller initialValue() {
try {
return jaxbContext.createMarshaller();
} catch (JAXBException e) {
throw new IllegalStateException("Unable to create marshaller");
}
}
};
private final JAXBContext jaxbContext;
private MyClassConstructor(){
try {
jaxbContext = JAXBContext.newInstance(Entity.class);
} catch (JAXBException e) {
throw new IllegalStateException("Unable to initialize");
}
}
}
Even better!! Based on the good solution from the post above, create the context just-once in the constructor, and save it instead of the class.
Replace the line:
private Class clazz;
with this one:
private JAXBContext jc;
And the main constructor with this one:
private Jaxb(Class clazz)
{
this.jc = JAXBContext.newInstance(clazz);
}
so in the getMarshaller/getUnmarshaller you can remove this line:
JAXBContext jc = JAXBContext.newInstance(clazz);
This improvement makes, in my case, that processing times drops from 60~70ms to just 5~10ms
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With