2010-11-18 6 views
2

私はxml解析を行う関数を持っています。私は関数スレッドを安全にしたいが、できるだけ最適化された(ブロッキングが少ない)ようにしたい。dom apiのJavaマルチスレッド

public Document doXML(InputStream s) 
{ 
//Some processing. 
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder parser = factory.newDocumentBuilder(); 
    Document xmlDoc = parser.parse(is); 
    return xmlDoc; 

} 

しかし、私は、各呼び出しで新しいのDocumentBuilderFactoryまたはDocumentBuilderのを作成する必要はありません。次のように短いコードで
は何かです。
私はファクトリとパーサを再利用したいが、スレッドセーフであるとは確信できません。だから最適なアプローチは何ですか?
1) DocumentBuilderFactoryをクラスフィールドにキャッシュし、factory.newDocumentBuilder()を同期させます。キャッシュのDocumentBuilderFactory てDocumentBuilderと同期parser.parse(である))は、各スレッドがDocumentBuilderの
2の独自のインスタンスを有するように、スレッドごとに
私は(2)が最適だと思いますが、それは安全ですか?また、同期してブロックすることもできますか?私はできるだけ早くそれをしたいと思います。

ありがとうございます。

+0

わかりません。このコードサンプルでは、​​synchronizedメソッドを使用する必要はありません。以来、あなたのリソースを保護するための共有オブジェクトはありません。共通の共通オブジェクトがある場合は、それらを相互に排他的にするために同期させる必要があります。 –

+0

@Mohamed Saligh:DocumentBuilderとDocumentBuilderFactoryを共有したいと思います。今すぐ、あなたは問題はありません。 – Cratylus

答えて

4

スレッドを再利用する場合(スレッドプール内など)、DocumentBuilderFactoryをスレッドローカルに宣言できます。スレッドごとに新しいセットを作成するオーバーヘッドがありますが、私が言ったように、あなたがreuisingしている場合、後続のオーバーヘッドは非常に低いです。

final ThreadLocal<DocumentBuilderFactory> documentBuilderFactor = new ThreadLocal<DocumentBuilderFactory>(){ 
    public DocumentBuilderFactory initialValue(){ 
     return DocumentBuilderFactory.newInstance(); 
    } 
} 

public Document doXML(InputStream s) 
{ 
//Some processing. 
    DocumentBuilderFactory factory = documentBuilderFactor.get(); 
    DocumentBuilder parser = factory.newDocumentBuilder(); 
    Document xmlDoc = parser.parse(is); 
    return xmlDoc; 

} 

ここでは、スレッドごとに1つのDocumentBuilderFactoryを作成します。

DocumentBuilderが解析時にスレッドセーフであるかどうかはわかりません(不変ですか?)。しかし、DocumentBuilderが構文解析時にスレッドセーフであれば、私が述べたのと同じメカニズムを使用できます。

この解像度は、全体のスループットをできるだけ速くします。

注:これはテストされていないか、コンパイルされているだけで私が何を参照しているのアイデアを与える。

+0

@John V.:私はこの機能を他の人たちと共に提供することになっています。スレッドはこの関数を呼び出しますが、どのように/いつそれらのスレッドが作成されるかは制御できません(私の部分ではありません)。スレッドが再利用されるかどうかはわかりません。スレッドが再利用されない場合、コードは私の元の投稿と同じですか? – Cratylus

+0

はい、あなたは正しいです。スレッドを再利用しない場合は、新しいDomファクトリ作成でThreadLocalを使用するオーバーヘッドが少し高くなります。それでは、私が思うたびに新しいスレッドを開始するのはもっと大きな問題です:) –

+0

@John V .:あなたはそうです!しかし、それらがスレッドをプールしていて、ローカルのスレッドを使用している場合、リークがあるかもしれませんか?これをチェックしてください:http://weblogs.java.net/blog/jjviana/archive/2010/06/09/dealing-glassfish-301-memory-leak-or-threadlocal-thread-pool-bad-ide – Cratylus

1

同期ブロッキングを避けるには、アトミック操作を使用する必要があります。 javax.xml.parser.*の動作は、実装によって異なります(システムプロパティを使用して実装を指定するか、実装コードを呼び出すことができます)。スレッド数と各スレッドの負荷重みに応じて、パーサーオブジェクトの作成を制御することが妥当かもしれません。新しいパーサーの作成か、パーサーの待機のどちらかを選択する必要があります。このコードは、開始時にパーサーのプールを作成してから、スレッドがプールからパーザを取得します。パーザは、フリーパーサーが存在しない場合にブロックされます。スレッドがパーサーを取得すると、データを解析し、パーサーをリセットしてプールに戻します。プールの長さによって、時間/メモリ使用量をいつでも制御できます。

+0

@khachik:「原子操作を確実に使うこと」をもっと詳しく教えてください。私はあなたが何を意味しているのか、そしてブロックするのを避けるためにそれをする方法を教えてください。 – Cratylus

+0

@ user384706 "atomic"は混乱するかもしれないので、XML解析には最適な言葉ではありませんハードウェアレイヤー(Compare-And-Swap)。最初の文は、2つのスレッドで同じパーサを使用することができないことを意味しているため、パーサーが1つのスレッドでのみ使用されるようにする必要があります。 – khachik

+0

@khachik:しかし、私はプールのサイズをどのように決定することができますか?私の理解は、numberOfProcessors + 1の最適なスレッド数はプールのサイズですか? – Cratylus

2

2)スレッドセーフですが、アプリは一度に1つのドキュメントしか解析できません。

あなたが持っているコードを使ってみませんか?ありますか

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
DocumentBuilder parser = factory.newDocumentBuilder(); 

明らかに受け入れられないオーバーヘッドはありますか?

+0

軽いテストでは問題はありません。しかし、負荷に応じて悪化するかどうかはわかりません。読んでから、私の理解は、工場を作ることは高価すぎるということです。私はこれもDocumentBuilderの略だと思います。だから私はこのコードが最終的にパフォーマンスが悪くなるかどうか疑問に思っていた – Cratylus

+0

ファクトリを作成するには*いくつかのオーバーヘッドがありますが、ストリームから読み込んでドキュメントを解析する場合と比べて重要ではありません。ほとんどのオーバーヘッドは、システムプロパティjaxb.properties(これは一度しかキャッシュされ、キャッシュされています)と最後にMETA-INF /サービスをチェックすることによって、実装クラスの名前を見つけるようです。オーバーヘッドを減らすには、実装クラス名を指定します。興味深いもの: – Qwerky

+0

しかし、どのように実装クラスを知っていますか?あなたはJohn Mの答えと同様のものを提案していますよね?どのように私のコードが実行されるランタイムで利用できるクラス名を知っていますか?あなたはそれを私に説明できますか? – Cratylus

1

同様の状況でパフォーマンスの問題が発生しました。私はスレッドの問題(1秒あたり10秒)を避けるために、それぞれの使用時にファクトリオブジェクトを作成していました。その(確かに古い)プラットフォームのXML実装は、サービスプロバイダクラスの比較的遅いルックアップロジックを実行しました。

私が調整したのは、結果として得られた答えを決定し、コマンドラインのプロパティで設定することでした。そのため、ルックアップはスキップされました。

-Djavax.xml.parsers.DocumentBuilderFactory=com.example.FactoryClassName 
-Djavax.xml.transform.TransformerFactory=com.example.OtherFactoryClassName 

クラスが見つかった場合、ルックアップコードがキャッシュロジックを持っていたというのは残念でした。しかし、ミスのキャッシングはありません(何も見つからず、デフォルトを使用します)。ネガティブなケースを処理していたルックアップキャッシュを少し改善すれば、これは不要になりました。

これはまだ必要ですか?あなたの環境でのテストが必要です。 Solaris上でtrussを使用して、そのルックアップロジックに起因する非常に頻繁なファイル操作に気付きました。

+0

この「トリック」は特定のプラットフォームでしか動作しませんが、コードは実行されるマシンの実際の実装クラスが何であるかをどのように知ることができますか? – Cratylus

+0

私の場合、ソースコードを読んでデフォルトの答えを見つけました。しかし、最初のステップは、あなたがこのような問題を抱えていたかどうかを確認することです。何か本当の利益を得ていない限り、このようなハードコーディングは使用しないでください。 –

+0

この問題のベンチマーキングに関する提案がありますか。 – Cratylus