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。我认为假设它不是线程安全的是最安全的。

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

XPath 对象是完全可重入和线程安全的。它们不包含用于评估的内部状态,因此可以轻松缓存并在应用程序中共享。一旦有了 XPath 对象,就可以将它应用于各种初始上下文并以几种不同的方式检索结果: --- SAX 路径和 Jaxen 简介

JAXEN Jira 对线程安全问题进行了各种修复,证明 Jaxen 被设计为线程安全的。这是偶然遇到的一个。并从其中一位作者那里确认Jaxen 是线程安全的。

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

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

一些入门资源: -使用 Jaxen 的示例。- Jaxen 常见问题解答主页

于 2010-06-01T22:28:33.220 回答
0

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

我将假设您的可运行文件的可执行部分(睡眠后的部分)花费不到一微秒,并且在您的测试运行中它们连续发生,而不是同时发生。因此,您的测试并不能真正证明任何事情。

为了进行更稳健的测试,我

  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 回答