JAXB создание контекста и стоимость маршаллеров
вопрос немного теоретический, какова стоимость создания контекста JAXB, marshaller и unmarshaller?
Я обнаружил, что мой код может извлечь выгоду из сохранения одного и того же контекста JAXB и, возможно, одного и того же маршаллера для всех операций маршалинга, а не создания контекста и маршаллера для каждого маршалинга.
Итак, какова стоимость создания контекста JAXB и marshaller/unmarshaller? Можно ли создать context+marshaller для каждой операции маршалинга или лучше этого избежать?
7 ответов:
Примечание: Я EclipseLink JAXB (MOXy) ведущий и член JAXB 2 (JSR-222) группы экспертов.
JAXBContext
является потокобезопасным и должен быть создан только один раз и повторно использоваться, чтобы избежать затрат на инициализацию метаданных несколько раз.Marshaller
иUnmarshaller
не потокобезопасны, но легки для создания и могут быть созданы за операцию.
в идеале, вы должны иметь синглтон
JAXBContext
и локальные экземплярыMarshaller
иUnmarshaller
.
JAXBContext
экземпляры потокобезопасны в то время какMarshaller
иUnmarshaller
экземпляров не потокобезопасный и никогда не должен быть разделен между потоками.
жаль, что это специально не описано в javadoc. Я могу сказать, что Spring использует глобальный JAXBContext, разделяемый между потоками, тогда как он создает новый маршаллер для каждой операции маршалинга с комментария javadoc в коде говорится, что маршаллеры JAXB не обязательно потокобезопасны.
то же самое сказано на этой странице:http://jaxb.java.net/guide/Performance_and_thread_safety.html.
Я думаю, что создание JAXBContext является дорогостоящей операцией, потому что она включает в себя сканирование классов и пакетов для аннотаций. Но измерение это лучший способ узнать.
Я решил эту проблему с помощью shared thread safe 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"); } }
JAXB 2.2 (JSR-222) имеет это сказать, в разделе "4.2 JAXBContext":
чтобы избежать накладных расходов, связанных с созданием
JAXBContext
экземпляр, a Приложение JAXB-это рекомендуется повторно использовать экземпляр JAXBContext. Один реализация абстрактного класса JAXBContext требуется потокобезопасный, таким образом, несколько потоков в приложении, можете поделиться тот же экземпляр JAXBContext.[..]
jaxbcontext класс разработан, чтобы быть неизменяемым и, следовательно, потокобезопасным. Учитывая объем динамической обработки, который потенциально может занять место при создании нового экземпляра JAXBContxt рекомендуется что экземпляр JAXBContext будет совместно использоваться в потоках и повторно использоваться как максимально повысить производительность приложения.
к сожалению, спецификация не делает никаких претензий относительно потокобезопасности
Unmarshaller
иMarshaller
. Поэтому лучше всего предположить, что это не так.
еще лучше!! Основываясь на хорошем решении из сообщения выше, создайте контекст только один раз в конструкторе и сохраните его вместо класса.
заменить строку:
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мс
Я обычно решаю такие проблемы с помощью
ThreadLocal
шаблон класса. Учитывая тот факт, что вам нужен другой маршаллер для каждого класса, вы можете объединить его сsingleton
-карты шаблон.чтобы сэкономить 15 минут работы. Здесь следует моя реализация потокобезопасной фабрики для Маршаллеров Jaxb и 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; } }