Answers:
注意: 我是EclipseLink JAXB(MOXy)的负责人,并且是JAXB 2(JSR-222)专家组的成员。
JAXBContext
是线程安全的,因此只能创建一次并重用,以避免多次初始化元数据的开销。 Marshaller
并且Unmarshaller
不是线程安全的,但是创建起来很轻巧,可以按操作创建。
理想情况下,你应该有一个单身JAXBContext
和本地实例Marshaller
和Unmarshaller
。
JAXBContext
实例是线程安全的,Marshaller
而Unmarshaller
实例不是线程安全的,并且永远不要在线程之间共享。
遗憾的是,javadoc中没有对此进行详细描述。我可以说的是,Spring使用了在线程之间共享的全局JAXBContext,而它为每个编组操作创建了一个新的编组器,并且在代码中有一个Javadoc注释,指出JAXB编组器不一定是线程安全的。
在此页面上也是如此:https : //javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety。
我猜想创建JAXBContext是一项昂贵的操作,因为它涉及扫描类和包以获取注释。但是测量它是了解的最佳方法。
JAXB 2.2(JSR-222)在“ 4.2 JAXBContext”部分中有这样的说法:
为了避免创建
JAXBContext
实例所涉及的开销,鼓励 JAXB应用程序重用JAXBContext实例。抽象类JAXBContext的实现必须是线程安全的,因此,应用程序中的多个线程可以共享同一JAXBContext实例。[..]
JAXBContext类被设计为不可变的,因此是线程安全的。考虑到创建新的JAXBContxt实例时可能发生的动态处理量,建议在线程之间共享JAXBContext实例,并尽可能重用它,以提高应用程序性能。
不幸的是,该规范没有对Unmarshaller
和的线程安全性提出任何要求Marshaller
。因此,最好假设它们不是。
我使用以下方法解决了这个问题:
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");
}
}
}
更好!!基于以上文章的好的解决方案,在构造函数中一次创建上下文,然后保存它而不是类。
替换行:
private Class clazz;
与此:
private JAXBContext jc;
以及与此相关的主要构造函数:
private Jaxb(Class clazz)
{
this.jc = JAXBContext.newInstance(clazz);
}
因此,在getMarshaller / getUnmarshaller中,您可以删除以下行:
JAXBContext jc = JAXBContext.newInstance(clazz);
就我而言,这种改进使得处理时间从60〜70ms减少到5〜10ms
我通常使用ThreadLocal
类模式解决此类问题。考虑到每个类都需要不同的编组器,可以将其与singleton
-map模式结合使用。
为您节省15分钟的时间。在此之后,我实现了针对Jaxb Marshallers和Unmarshallers的线程安全Factory。
它允许您按以下方式访问实例...
Marshaller m = Jaxb.get(SomeClass.class).getMarshaller();
Unmarshaller um = Jaxb.get(SomeClass.class).getUnmarshaller();
您将需要的代码是一个小Jaxb类,如下所示:
public class Jaxb
{
// singleton pattern: one instance per class.
private static Map<Class,Jaxb> singletonMap = new HashMap<>();
private Class clazz;
// thread-local pattern: one marshaller/unmarshaller instance per thread
private ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<>();
private ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<>();
// The static singleton getter needs to be thread-safe too,
// so this method is marked as synchronized.
public static synchronized Jaxb get(Class clazz)
{
Jaxb jaxb = singletonMap.get(clazz);
if (jaxb == null)
{
jaxb = new Jaxb(clazz);
singletonMap.put(clazz, jaxb);
}
return jaxb;
}
// the constructor needs to be private,
// because all instances need to be created with the get method.
private Jaxb(Class clazz)
{
this.clazz = clazz;
}
/**
* Gets/Creates a marshaller (thread-safe)
* @throws JAXBException
*/
public Marshaller getMarshaller() throws JAXBException
{
Marshaller m = marshallerThreadLocal.get();
if (m == null)
{
JAXBContext jc = JAXBContext.newInstance(clazz);
m = jc.createMarshaller();
marshallerThreadLocal.set(m);
}
return m;
}
/**
* Gets/Creates an unmarshaller (thread-safe)
* @throws JAXBException
*/
public Unmarshaller getUnmarshaller() throws JAXBException
{
Unmarshaller um = unmarshallerThreadLocal.get();
if (um == null)
{
JAXBContext jc = JAXBContext.newInstance(clazz);
um = jc.createUnmarshaller();
unmarshallerThreadLocal.set(um);
}
return um;
}
}