DocumentBuilder线程安全吗?

CKi*_*ing 36 java performance singleton multithreading dom

我正在查看的当前代码库使用DOM解析器.以下代码片段在5种方法中重复:

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 DocumentBuilder builder = factory.newDocumentBuilder();
Run Code Online (Sandbox Code Playgroud)

如果在循环中调用包含上述代码的方法,或者在应用程序中多次调用该方法,那么我们将承担为每次调用此类方法创建新的DocumentBuilderFactory实例和新的DocumentBuilder实例的开销.

在DocumentBuilder工厂和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);
   }
}
Run Code Online (Sandbox Code Playgroud)

如果上述单例在多个线程之间共享,是否会出现任何问题?如果没有,通过在应用程序的整个生命周期中使用上述创建DocumentBuilderFactory和DocumentBuilder实例的方法,是否会有任何性能提升?

编辑:

我们唯一能遇到问题的是,如果DocumentBuilder在解析可能影响解析下一个XML文件的XML文件时保存了一些状态信息.

Den*_*kiy 35

有关同一事项的其他问题,请参阅评论部分.对你的问题的简短回答:不,把这些类放在一个单例中是不行的.DocumentBuilderFactory和DocumentBuilder都不保证是线程安全的.如果您有多个解析XML的线程,请确保每个线程都有自己的DoumentBuilder版本.每个线程只需要其中一个,因为重置后可以重用DocumentBuilder.

编辑一个小片段,表明使用相同的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();
Run Code Online (Sandbox Code Playgroud)

所以这是你的答案 - 不要DocumentBuilder.parse()从多个线程调用.是的,这种行为可能是JRE特定的,如果你使用的是IBM java或JRockit,或者给它一个不同的DocumentBuilderImpl,它可能工作正常,但对于默认的xerces实现 - 它没有.

  • @bot:这意味着解析方法是*不是*线程安全的.DocumentBuilderFactory应该没有问题,但是由于你需要一些解析文件的单例,我建议你创建一个DocumentBuilders池并从中使用它们.有关简单的实现,请参阅http://commons.apache.org/pool/. (3认同)
  • @bot:是的,但解析可能也不是线程安全的.在`DocumentBuilder`上有一个`reset()`方法的事实可能表明解析会改变一些东西,从多个线程调用它不是一个好主意. (2认同)

小智 16

JAXP规范(V 1.4)说:

预计SAXParserFactory实现的newSAXParser方法,DocumentBuilderFactory的newDocumentBuilder方法和TransformerFactory的newTransformer方法将是线程安全的,没有副作用.这意味着应用程序员应该能够从共享工厂一次创建多个线程中的转换器实例,而不会产生副作用或问题.

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

因此,例如,您应该能够通过DocumentBuilderFactory.newInstance创建单个DocumentBuilderFactory实例,然后使用该单个工厂通过DocumentBuilderFactory.newDocumentBuilder为每个线程创建一个DocumentBuilder.您还可以创建一个DocumentBuilders池.

我找不到任何说法,例如,静态方法DocumentBuilderFactory.newInstance是线程安全的.该实现看起来是线程安全的,因为有一些方法同步正在完成,但规范明确指出DocumentBuilderFactory.newDocumentBuilder是线程安全的.