Могу ли я безопасно запросить документ DOM с выражением xpath из нескольких потоков? - PullRequest
3 голосов
/ 28 мая 2010

Я планирую использовать DOM4J 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 ]

5 голосов
/ 02 июня 2010

http://xerces.apache.org/xerces2-j/faq-dom.html говорит

Нет. ДОМ не требует реализации должны быть потокобезопасными. Если вам нужно получить доступ к DOM из несколько потоков, вы обязаны добавьте соответствующие замки к вашему код приложения.

Не видя реализации, невозможно узнать, использует ли selectSingleNode какое-либо общее состояние для чтения DOM. Я думаю, что безопаснее предположить, что это не потокобезопасно.

Альтернативой является использование собственного процессора XPath, такого как Jaxen, который является поточно-ориентированным.

Объекты XPath полностью реентерабельны и потокобезопасный. Они не содержат внутренних состояние для оценки и, следовательно, может быть легко кэшируется и разделяется внутри приложение. Как только у вас есть XPath объект, вы можете применить его против различные начальные контексты и извлекать результаты в нескольких разных направлениях: --- Введение в SAX path и Jaxen

JAXEN Jira имеет различные исправления для проблем с многопоточностью, свидетельствующие о том, что Jaxen спроектирован как поточно-ориентированный. Это один Я случайно наткнулся. И подтверждение того, что Jaxen является поточно-ориентированным от одного из авторов.

Наряду с поточной безопасностью, Jaxen не зависит от модели - он работает со многими моделями (W3C DOM, XOM, Dom4J, JDOM), и пользовательские модели можно подключить, реализовав пару интерфейсов.

Я бы предположил, что простые методы доступа и итераторы в DOM W3C являются потокобезопасными. Но это только догадка, а не конкретный факт. Если вы хотите быть на 100% уверенным, то используйте DOM, предназначенный для безопасности потоков, например, dom4j .

Некоторые ресурсы для начала: - Пример использования Jaxen . - Jaxen FAQ и домашняя страница

0 голосов
/ 29 мая 2010

Я на самом деле не знаком с 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();
}

Если это работает как предсказано (это не скомпилировано и не проверено), то оно будет продолжать генерировать новые потоки, новые потоки будут пытаться прочитать документ. Они сообщат о возможном конфликте времени с другим потоком. Они сообщат, если они прочитают неправильное значение. Он будет генерировать новые потоки до тех пор, пока в вашей системе не закончатся ресурсы, а затем произойдет сбой.

...