回答:
注: 私はEclipseLink JAXB(MOXy)のリーダーであり、JAXB 2(JSR-222)エキスパートグループのメンバーです。
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〜70ミリ秒から5〜10ミリ秒に短縮されます。
私は通常、このような問題をThreadLocal
クラスパターンで解決します。クラスごとに異なるマーシャラーが必要であるという事実を踏まえて、それをsingleton
-mapパターンと組み合わせることができます。
作業時間を15分節約できます。Jaxbマーシャラーとアンマーシャラー用のスレッドセーフなファクトリーの実装を次に示します。
次のようにインスタンスにアクセスできます...
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;
}
}