Подтвердить что ты не робот

Безопасен ли поток DocumentBuilder?

В текущей базе кода, которую я ищу, используется парсер DOM. Следующий фрагмент кода дублируется в 5 методах:

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

Если метод, содержащий указанный выше код, вызывается в цикле или метод вызывается несколько раз в приложении, мы несли накладные расходы на создание нового экземпляра DocumentBuilderFactory и нового экземпляра DocumentBuilder для каждого вызова такого метода,

Было бы неплохо создать обертку singleton вокруг экземпляров DocumentBuilder factory и 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);
   }
}

Существуют ли какие-либо проблемы, которые могут возникнуть, если вышеописанный singleton используется для нескольких потоков? Если нет, будет ли какой-либо прирост производительности за счет использования вышеприведенного подхода для создания экземпляров DocumentBuilderFactory и DocumentBuilder только один раз на протяжении всего срока службы приложения?

Изменить:

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

4b9b3361

Ответ 1

См. раздел комментариев для других вопросов по одному и тому же вопросу. Короткий ответ на ваш вопрос: нет, это не ok, чтобы поставить эти классы в одноэлементный. Ни DocumentBuilderFactory, ни DocumentBuilder не гарантируются потоком. Если у вас есть несколько потоков, анализирующих XML, убедитесь, что каждый поток имеет свою собственную версию DoumentBuilder. Вам нужно только один из них на поток, поскольку вы можете повторно использовать DocumentBuilder после reset.

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, если вы используете IBM java или JRockit или предоставляете ему другой DocumentBuilderImpl, он может работать нормально, но для реализации по умолчанию xerces - это не так.

Ответ 2

Спецификация JAXP (V 1.4) гласит:

Ожидается, что метод newSAXParser реализации SAXParserFactory, метод newDocumentBuilder для DocumentBuilderFactory и метод newTransformer для TransformerFactory будут потокобезопасными без побочных эффектов. Это означает, что программист-программист должен ожидать, что он сможет создавать экземпляры трансформатора в нескольких потоках одновременно из общего factory без побочных эффектов или проблем.

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

Итак, например, вы должны создать один экземпляр DocumentBuilderFactory через DocumentBuilderFactory.newInstance, а затем использовать этот единственный factory для создания DocumentBuilder для каждого потока через DocumentBuilderFactory.newDocumentBuilder. Вы также можете создать пул DocumentBuilders.

Я не могу найти нигде, который говорит, что, например, статический метод DocumentBuilderFactory.newInstance является потокобезопасным. Реализация выглядит как потокобезопасная, поскольку выполняется некоторая синхронизация метода, но спецификация специально говорит о том, что DocumentBuilderFactory.newDocumentBuilder является потокобезопасным.

Ответ 3

Вам нужно знать три вещи:

  • Какова стоимость создания factory? Если стоимость низкая, ваш коэффициент производительности может быть близок к нулю.
  • Какова стоимость создания строителя? Если стоимость низкая, ваш коэффициент производительности может быть близок к нулю.
  • Безопасен ли поток factory и/или строителя? Если это не так, вам нужно убедиться, что доступ к ним метода безопасен с помощью ключевого слова synchronized.

Я не знаком с используемыми вами классами DocumentBuilder, но вся эта информация должна быть доступна в его javadoc или другой документации. Если создание определенных объектов является дорогостоящим, они обычно бросают эту информацию на вас.