JAXB создает контекст и стоимость маршаллеров - PullRequest
105 голосов
/ 13 сентября 2011

Вопрос немного теоретический, какова стоимость создания контекста JAXB, маршаллера и демаршаллера?

Я обнаружил, что мой код может выиграть от сохранения того же контекста JAXB и, возможно, того же маршаллеравсе операции маршалинга вместо создания контекста и маршаллера на каждом маршалинге.

Так какова стоимость создания контекста JAXB и маршаллера / демаршаллера?Можно ли создавать контекст + маршаллер для каждой операции маршалинга или лучше избегать этого?

Ответы [ 7 ]

219 голосов
/ 13 сентября 2011

Примечание: Я EclipseLink JAXB (MOXy) и член JAXB 2 ( JSR-222 ) экспертная группа.

JAXBContext является потокобезопасным и должен создаваться только один раз и использоваться повторно, чтобы избежать затрат на инициализацию метаданных несколько раз. Marshaller и Unmarshaller не являются поточно-ориентированными, но они легки в создании и могут быть созданы для каждой операции.

37 голосов
/ 13 сентября 2011

В идеале у вас должен быть синглтон JAXBContext и локальные экземпляры Marshaller и Unmarshaller.

JAXBContext экземпляры являются поточно-ориентированными, в то время как Marshaller и Unmarshaller экземпляры не поточнобезопасны и никогда не должны совместно использоваться потоками.

13 голосов
/ 13 сентября 2011

Жаль, что это конкретно не описано в javadoc.Что я могу сказать, так это то, что Spring использует глобальный JAXBContext, совместно используемый потоками, тогда как он создает новый маршаллер для каждой операции маршаллинга с комментарием javadoc в коде, говорящем, что маршалеры JAXB не обязательно являются поточно-ориентированными.

То же самое сказано на этой странице: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety.

Я бы предположил, что создание JAXBContext является дорогостоящей операцией, поскольку включает в себя сканирование классов и пакетов для аннотаций.Но измерение это лучший способ узнать.

3 голосов
/ 20 июня 2016

JAXB 2.2 ( JSR-222 ) имеет это сказать в разделе "4.2 JAXBContext":

Чтобы избежать накладных расходов, связанных с созданием экземпляра JAXBContext, Приложению JAXB рекомендуется повторно использовать экземпляр JAXBContext . реализация абстрактного класса JAXBContext должна быть поточно-ориентированный , таким образом, несколько потоков в приложении могут совместно использовать тот же экземпляр JAXBContext.

[..]

Класс JAXBContext разработан для того, чтобы быть неизменным и, следовательно, потокобезопасным. Учитывая количество динамической обработки, которое потенциально может занять место при создании нового экземпляра JAXBContxt, рекомендуется что экземпляр JAXBContext будет разделен между потоками и повторно использован как максимально улучшить производительность приложений.

К сожалению, спецификация не претендует на безопасность потоков Unmarshaller и Marshaller. Поэтому лучше предположить, что это не так.

3 голосов
/ 18 сентября 2013

Я решил эту проблему, используя общий потокобезопасный JAXBContext и локальный поток un / marschallers (теоретически, будет un / marshaller экземпляров столько, сколько существует потоков, которые к ним обращались) с синхронизацией только при инициализации 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");
    }
}
1 голос
/ 29 января 2014

Еще лучше !!Основываясь на хорошем решении из поста выше, создайте контекст только один раз в конструкторе и сохраните его вместо класса.

Заменить строку:

  private Class clazz;

на эту:

  private JAXBContext jc;

И главный конструктор на эту:

  private Jaxb(Class clazz)
  {
     this.jc = JAXBContext.newInstance(clazz);
  }

такв getMarshaller / getUnmarshaller вы можете удалить эту строку:

  JAXBContext jc = JAXBContext.newInstance(clazz);

В этом случае, в моем случае, время обработки уменьшается с 60 ~ 70 мс до всего 5 ~ 10 мс

0 голосов
/ 08 сентября 2013

Обычно я решаю подобные проблемы с помощью шаблона класса 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;
  }
}
...