我可以安全地从多个线程查询带有xpath表达式的DOM Document吗?

时间:2010-05-28 15:12:16

标签: java xml xpath concurrency dom4j

我计划在多个线程可以查询文档的应用程序中使用dom4j DOM Document作为静态缓存。 考虑到文档本身永远不会改变的帐户,从多个线程查询它是否安全?

我编写了以下代码来测试它,但我不确定它确实证明操作是安全的吗?

    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!" );
        }    

    }

2 个答案:

答案 0 :(得分:5)

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

  

没有。 DOM不需要   实现是线程安全的。如果   你需要从中访问DOM   你需要多个线程   添加适当的锁定到您的   应用程序代码。

如果没有看到实现,就不可能知道selectSingleNode是否使用任何共享状态来读取DOM。我认为最安全的做法是假设它不是线程安全的。

另一种方法是使用自己的XPath处理器,例如Jaxen,它是线程安全的。

  

XPath对象是完全可重入的   线程安全的。它们不包含内部   评估的状态,因此可以   缓存缓存并在内部共享   应用。一旦你有XPath   对象,你可以应用它   各种初始上下文和检索   导致几种不同的方式:     --- Introduction to SAX path and Jaxen

JAXEN Jira针对线程安全问题提供了各种修复,提供了Jaxen设计为线程安全的证据。这是one我偶然遇到的。 并且confirmation Jaxen对其中一位作者来说是线程安全的。

除了线程安全之外,Jaxen与模型无关 - 它适用于许多模型(W3C DOM,XOM,Dom4J,JDOM),并且可以通过实现几个接口来插入自定义模型。

我认为W3C DOM上的简单访问器和迭代器是线程安全的。但这只是一种预感,而不是一个具体的事实。如果您想100%确定,那么请使用专为线程安全设计的DOM,例如dom4j

一些入门资源: - An example of using Jaxen。 - Jaxen FAQhomepage

答案 1 :(得分:0)

我实际上并不熟悉dom4j DOM,但如果你不确定它是否可以正确处理只读数据,我不确定它有多好。

我将做出操作假设,即runnables的可执行部分(睡眠后的部分)花费不到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!");
            }
        }
    }
    

    }

  4. 然后

    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();
    }
    

    如果它按预期工作(这是未编译和未经测试的),那么它将继续生成新线程,新线程将尝试读取文档。他们将报告他们是否可能与另一个线程发生冲突。如果他们读错了值,他们会报告。它将继续生成新线程,直到您的系统资源耗尽,然后它将崩溃。