질문은 약간 이론적입니다. JAXB 컨텍스트, 마샬 러 및 언 마샬 러를 만드는 데 드는 비용은 얼마입니까?
내 코드는 각 마샬링에서 컨텍스트와 마샬 러를 생성하는 것보다 모든 마샬링 작업에 대해 동일한 JAXB 컨텍스트 및 가능하면 동일한 마샬 러를 유지하는 것이 도움이 될 수 있음을 발견했습니다.
그렇다면 JAXB 컨텍스트와 마샬 러 / 언 마샬 러를 만드는 데 드는 비용은 얼마입니까? 각 마샬링 작업에 대해 컨텍스트 + 마샬 러를 만드는 것이 괜찮습니까? 아니면 피하는 것이 더 낫습니까?
답변
참고 : 저는 EclipseLink JAXB (MOXy) 리더이자 JAXB 2 ( JSR-222 ) 전문가 그룹의 구성원입니다.
JAXBContext
스레드로부터 안전하며 메타 데이터를 여러 번 초기화하는 비용을 피하기 위해 한 번만 생성하고 재사용해야합니다. Marshaller
및 Unmarshaller
스레드에 안전하지만, 만들 경량 및 작업 당 생성 할 수 없습니다.
답변
이상적으로, 당신은 싱글이 있어야 JAXBContext
하고 로컬 인스턴스 Marshaller
와 Unmarshaller
.
JAXBContext
인스턴스는 스레드 안전 동안입니다 Marshaller
및 Unmarshaller
인스턴스가 없는 스레드 안전하고 스레드에서 공유해서는 안됩니다.
답변
이것이 javadoc에서 구체적으로 설명되지 않은 것은 유감입니다. 내가 말할 수있는 것은 Spring이 스레드간에 공유되는 전역 JAXBContext를 사용하는 반면, 각 마샬링 작업에 대해 새 마샬 러를 생성 하고 코드에 JAXB 마샬 러가 반드시 스레드로부터 안전하지 않다는 내용 의 javadoc 주석 을 생성한다는 것입니다.
이 페이지에서도 마찬가지입니다 : 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
. 따라서 그렇지 않다고 가정하는 것이 가장 좋습니다.
답변
다음을 사용하여이 문제를 해결했습니다.
- 공유 스레드 안전 JAXBContext 및 스레드 로컬 un / marschallers
- (이론적 으로는 액세스 한 스레드 수만큼 un / marshaller 인스턴스가 있습니다.)
- un / 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를위한 스레드 안전 팩토리 구현을 따릅니다.
다음과 같이 인스턴스에 액세스 할 수 있습니다.
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;
}
}