it-swarm.cn

JAXB创建上下文和marshallers成本

问题有点理论化,创建JAXB上下文,marshaller和unmarshaller的成本是多少?

我发现我的代码可以从保持相同的JAXB上下文和可能与所有编组操作相同的编组器中受益,而不是在每个编组时创建上下文和编组器。

那么创建JAXB上下文和marshaller/unmarshaller的成本是多少?是否可以为每个编组操作创建context + marshaller,或者最好避免它?

99
Vladimir

注意: 我是 EclipseLink JAXB(MOXy) lead和JAXB 2的成员( JSR-222 )专家组。

JAXBContext是线程安全的,只应创建一次并重复使用,以避免多次初始化元数据的成本。 MarshallerUnmarshaller不是线程安全的,但是要创建轻量级,并且可以按操作创建。

209
bdoughan

理想情况下,你应该有一个单例JAXBContextMarshallerUnmarshaller的本地实例。

JAXBContext实例是线程安全的,而MarshallerUnmarshaller实例是 not thread-safe,不应该跨线程共享。

34
Sahil Muthoo

遗憾的是,这在javadoc中没有具体描述。我能说的是Spring使用了一个全局JAXBContext,在线程之间共享,而它为每个编组操作创建了一个新的编组器,在代码中用 javadoc注释 表示JAXB编组器不一定是线程安全的。

本页也是如此: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety

我猜想创建一个JAXBContext是一项代价高昂的操作,因为它涉及扫描类和注释的包。但测量它是最好的方法。

12
JB Nizet

我解决了这个问题使用共享线程安全 JAXBContext 和线程本地 un/marschallers (所以理论上,会有尽可能多的 un/marshaller instances,因为有线程访问它们) 同步仅在 un/marshaller 的初始化。

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");
    }
}
3
peeeto

JAXB 2.2( JSR-222 )在“4.2 JAXBContext”一节中有这个说法:

为了避免创建 JAXBContext instance所涉及的开销,鼓励JAXB应用程序重用JAXBContext实例。抽象类JAXBContext 的实现需要是线程安全的,因此,应用程序中的多个线程可以共享同一个JAXBContext实例。

[..]

JAXBContext类被设计为不可变的,因此线程安全。考虑到在创建JAXBContxt的新实例时可能发生的动态处理量,建议跨线程共享JAXBContext实例并尽可能重用以提高应用程序性能。

不幸的是,该规范没有对 UnmarshallerMarshaller 的线程安全做出任何声明。所以最好假设它们不是。

3
Martin Andersson

更好!!基于上面帖子中的好解决方案,在构造函数中创建context just-once ,并保存它而不是类。

替换线:

  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

1
tbarderas

我通常使用ThreadLocal类模式来解决这样的问题。鉴于每个类需要一个不同的编组器,您可以将它与singleton- map模式组合在一起。

为你节省15分钟的工作量。以下是我对Jaxb Marshallers和Unmarshallers的线程安全工厂的实现。

它允许您按如下方式访问实例...

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;
  }
}
0
bvdb