3

xml 解析を行う関数があります。関数をスレッドセーフにしたいだけでなく、可能な限り最適化 (ブロッキングを少なく) したいと考えています。
短いコードでは、次のようなものです。

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(); を同期します。各スレッドが DocumentBuilder の独自のインスタンスを持つようにします。
2) DocumentBuilderFactoryDocumentBuilder をキャッシュし、parser.parse(is); を同期します。スレッドごと
に (2) が最適だと思いますが、安全に実行できますか? また、同期によるブロックを回避できますか? できるだけ速くしたいと思います。

ありがとう?

4

4 に答える 4

5

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

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;

}

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

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

この解像度により、全体的なスループットが可能な限り高速になります。

注:これはテストもコンパイルもされていないため、私が言及していることのアイデアが得られます。

于 2010-11-18T17:13:19.303 に答える
2

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

あなたが持っているコードを使用しないのはなぜですか?する

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

明らかに許容できないオーバーヘッドがありますか?

于 2010-11-18T17:01:27.430 に答える
1

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

于 2010-11-18T16:59:06.287 に答える
1

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

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

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

苛立たしいのは、クラスが見つかった場合にルックアップ コードにキャッシュ ロジックが含まれていたことです。ただし、ミスのキャッシュはありません (何も見つかりませんでした。デフォルトを使用してください)。否定的なケースを処理するわずかに優れたルックアップ キャッシュにより、これは不要になります。

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

于 2010-11-18T18:15:59.103 に答える