web-dev-qa-db-ja.com

DocumentBuilderスレッドは安全ですか?

私が見ている現在のコードベースは、DOMパーサーを使用しています。次のコードは、5つのメソッドで複製されています。

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

上記のコードを含むメソッドがループで呼び出される場合、またはメソッドがアプリケーションで複数回呼び出される場合、そのようなメソッドの呼び出しごとに新しいDocumentBuilderFactoryインスタンスと新しいDocumentBuilderインスタンスを作成するオーバーヘッドが発生します。

以下に示すように、DocumentBuilderファクトリとDocumentBuilderインスタンスの周囲にシングルトンラッパーを作成することをお勧めします。

public final class DOMParser {
   private DocumentBuilderFactory = new DocumentBuilderFactory();
   private DocumentBuilder builder;

   private static DOMParser instance = new DOMParser();

   private DOMParser() {
      builder = factory.newDocumentBuilder();
   }

   public Document parse(InputSource xml) {
       return builder.parser(xml);
   }
}

上記のシングルトンが複数のスレッド間で共有される場合に発生する可能性のある問題はありますか?そうでない場合、DocumentBuilderFactoryとDocumentBuilderインスタンスを作成する上記のアプローチをアプリケーションのライフタイム全体で一度だけ使用することにより、パフォーマンスが向上しますか?

編集:

問題に直面することができるのは、次のXMLファイルの解析に影響する可能性のあるXMLファイルの解析中にDocumentBuilderが状態情報を保存する場合のみです。

34
CKing

同じ問題に関する他の質問については、コメントのセクションをご覧ください。 質問に対する簡単な答え:いいえ、そうではありませんこれらのクラスをシングルトンに入れることは問題ありません。 DocumentBuilderFactoryもDocumentBuilderも、スレッドセーフであることは保証されていません。 XMLを解析するスレッドが複数ある場合は、各スレッドが独自のバージョンのDoumentBuilderを持っていることを確認してください。 DocumentBuilderはリセット後に再利用できるため、必要なのはスレッドごとに1つだけです。

[〜#〜] edit [〜#〜]同じDocumentBuilderの使用が悪いことを示す小さなスニペット。 Java 1.6_u32および1.7_u05を使用すると、このコードは_org.xml.sax.SAXException: FWK005 parse may not be called while parsing_で失敗します。ビルダーで同期を解除し、正常に動作します。

_        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        final DocumentBuilder builder = factory.newDocumentBuilder();

        ExecutorService exec = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            exec.submit(new Runnable() {
                public void run() {
                    try {
//                        synchronized (builder) {
                            InputSource is = new InputSource(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\" ?><俄语>данные</俄语>"));
                            builder.parse(is);
                            builder.reset();
//                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        exec.shutdown();
_

答えは次のとおりです。複数のスレッドからDocumentBuilder.parse()を呼び出さないでください。はい、この動作はJRE固有である可能性があります。IBMJavaまたはJRockitを使用している場合、または別のDocumentBuilderImplを指定している場合、正常に動作する可能性がありますが、デフォルトのxerces実装では-動作しません。

33
Denis Tulskiy

JAXP仕様(V 1.4)には次のように書かれています。

SAXParserFactory実装のnewSAXParserメソッド、DocumentBuilderFactoryのnewDocumentBuilderメソッド、TransformerFactoryのnewTransformerメソッドは、副作用なしでスレッドセーフになることが期待されています。これは、アプリケーションプログラマが、副作用や問題なしに共有ファクトリから複数のスレッドでトランスフォーマインスタンスを一度に作成できることを期待する必要があることを意味します。

https://jaxp.Java.net/docs/spec/html/#plugabililty-thread-safety

したがって、たとえば、DocumentBuilderFactory.newInstanceを介して単一のDocumentBuilderFactoryインスタンスを作成し、その単一のファクトリを使用して、DocumentBuilderFactory.newDocumentBuilderを介してスレッドごとにDocumentBuilderを作成できる必要があります。 DocumentBuilderのプールを作成することもできます。

たとえば、静的メソッドDocumentBuilderFactory.newInstanceはスレッドセーフであるということはどこにもありません。実装はいくつかのメソッド同期が行われるという点でスレッドセーフに見えますが、仕様では特にDocumentBuilderFactory.newDocumentBuilderがスレッドセーフであると述べています。

16
ttt

次の3つのことを知る必要があります。

  1. 工場を作る費用はいくらですか?コストが低い場合、パフォーマンスの向上はほぼゼロになる可能性があります。
  2. ビルダーを作成するコストはいくらですか?コストが低い場合、パフォーマンスの向上はほぼゼロになる可能性があります。
  3. 工場やビルダーのスレッドは安全ですか?そうでない場合は、synchronizedキーワードを使用して、それらにアクセスするメソッドをスレッドセーフにする必要があります。

私はあなたが使用しているDocumentBuilderクラスに精通していませんが、これらすべての情報はjavadocまたは他のドキュメントで入手できるはずです。特定のオブジェクトの作成に費用がかかる場合、通常、この情報はユーザーに投げられます。

2
Rasmus Franke