3

複数のスレッドがドキュメントをクエリできるアプリケーションで、dom4j DOM ドキュメントを静的キャッシュとして使用する予定です。ドキュメント自体が決して変更されないことを考慮して、複数のスレッドからクエリを実行しても安全ですか?

テストするために次のコードを書きましたが、実際に動作が安全であることを証明できるかどうかはわかりません。

    package test.concurrent_dom;

    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.DocumentHelper;
    import org.dom4j.Element;
    import org.dom4j.Node;

    /**
     * Hello world!
     *
     */
    public class App extends Thread
    {
        private static final String xml = 
            "<Session>"
                + "<child1 attribute1=\"attribute1value\" attribute2=\"attribute2value\">"
                + "ChildText1</child1>"
                + "<child2 attribute1=\"attribute1value\" attribute2=\"attribute2value\">"
                + "ChildText2</child2>" 
                + "<child3 attribute1=\"attribute1value\" attribute2=\"attribute2value\">"
                + "ChildText3</child3>"
            + "</Session>";

        private static Document document;

        private static Element root;

        public static void main( String[] args ) throws DocumentException
        {
            document = DocumentHelper.parseText(xml);
            root = document.getRootElement();

            Thread t1 = new Thread(){
                public void run(){
                    while(true){

                        try {
                            sleep(3);
                        } catch (InterruptedException e) {                  
                            e.printStackTrace();
                        }

                        Node n1 = root.selectSingleNode("/Session/child1");                 
                        if(!n1.getText().equals("ChildText1")){                     
                            System.out.println("WRONG!");
                        }
                    }
                }
            };

            Thread t2 = new Thread(){
                public void run(){
                    while(true){

                        try {
                            sleep(3);
                        } catch (InterruptedException e) {                  
                            e.printStackTrace();
                        }

                        Node n1 = root.selectSingleNode("/Session/child2");                 
                        if(!n1.getText().equals("ChildText2")){                     
                            System.out.println("WRONG!");
                        }
                    }
                }
            };

            Thread t3 = new Thread(){
                public void run(){
                    while(true){

                        try {
                            sleep(3);
                        } catch (InterruptedException e) {                  
                            e.printStackTrace();
                        }

                        Node n1 = root.selectSingleNode("/Session/child3");                 
                        if(!n1.getText().equals("ChildText3")){                     
                            System.out.println("WRONG!");
                        }
                    }
                }
            };

            t1.start();
            t2.start();
            t3.start();
            System.out.println( "Hello World!" );
        }    

    }
4

2 に答える 2

5

http://xerces.apache.org/xerces2-j/faq-dom.html言う

いいえ。DOM では、実装がスレッド セーフである必要はありません。複数のスレッドから DOM にアクセスする必要がある場合は、アプリケーション コードに適切なロックを追加する必要があります。

selectSingleNode実装を見ないと、DOM の読み取りに共有状態を使用しているかどうかを知ることは不可能です。スレッドセーフではないと仮定するのが最も安全だと思います。

別の方法は、スレッドセーフな Jaxen などの独自の XPath プロセッサを使用することです。

XPath オブジェクトは完全に再入可能で、スレッドセーフです。それらには評価用の内部状態が含まれていないため、簡単にキャッシュしてアプリケーション内で共有できます。XPath オブジェクトを取得したら、さまざまな初期コンテキストに対してそれを適用し、いくつかの異なる方法で結果を取得できます。 --- SAX パスと Jaxen の紹介

JAXEN Jira には、スレッド セーフの問題に対するさまざまな修正が含まれており、Jaxen がスレッド セーフになるように設計されていることを証明しています。たまたま出逢った一枚です。また、著者の 1 人から Jaxen がスレッドセーフであることが確認されました。

スレッドセーフであるだけでなく、Jaxen はモデルに依存しません。多くのモデル (W3C DOM、XOM、Dom4J、JDOM) で動作し、いくつかのインターフェースを実装することでカスタム モデルをプラグインできます。

W3C DOM の単純なアクセサーとイテレーターはスレッドセーフだと思います。しかし、これは単なる憶測であり、具体的な事実ではありません。100% 確実にしたい場合は、スレッドセーフ用に設計されたdom4jなどの DOM を使用してください。

開始するためのリソース: - Jaxen の使用例。- Jaxen FAQホームページ

于 2010-06-01T22:28:33.220 に答える
0

私は実際には dom4j DOM に精通していませんが、それが読み取り専用データを適切に処理できるかどうかわからない場合、それがどれほど優れているかはわかりません。

ランナブルの実行可能部分 (スリープ後の部分) が 1 マイクロ秒未満であり、テスト実行ではそれらが同時にではなく連続して発生したという運用上の仮定を立てます。したがって、あなたのテストは実際には何も証明しません。

より堅牢なテストのために、私は

  1. 3 マイクロ秒のスリープを排除しました。テスト コードは、スリープ状態ではなく、競合の可能性を生成するためにビジーである必要があります。
  2. スレッド数を増やしました - 同時に実行するスレッドが多いほど、可能性が高くなります
  3. プリミティブ競合検出を追加

    final AtomicReference<Thread>owner=new AtomicReference<Thread>() ;
    class TestThread
    {
        private String url ;
        private String expected ;
        TestThread(int index) { url = "/Session/child" + i ; expected="ChildText" + i ; }
        public String toString () {return expected;}
        public void run()
        {
            while(true)
            {
                boolean own=owner.compareAndSet(null,this);
                Node n1 = root.selectSingleNode(url);                 
                boolean wrong = !n1.getText().equals(result);
                owner.compareAndSet(this,null);
                if(!own) { System.out.println ( owner.get() + " conflicts " + this ) }
                if(wrong){System.out.println(this + " WRONG!");
            }
        }
    }
    

    }

それから

try{
    while(true){
    Thread t1 = new TestThread(1);
    t1.start();
    Thread t2 = new TestThread(2);
    t2.start();
    Thread t3 = new TestThread(3);
    t3.start();
    }
}
catch(Throwable thr){
    thr.printStackTrace();
}

予測どおりに動作する場合 (これはコンパイルもテストもされていません)、新しいスレッドを生成し続け、新しいスレッドはドキュメントを読み取ろうとします。別のスレッドと時間的に競合する可能性がある場合は報告します。間違った値を読み取った場合は報告されます。システムのリソースがなくなるまで新しいスレッドを生成し続け、その後クラッシュします。

于 2010-05-29T00:18:46.577 に答える