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

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

如果包含上述代码的方法在循环中被调用,或者该方法在应用程序中被多次调用,则我们将承担为每个此类方法的调用创建一个新的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);
   }
}

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

编辑:

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

最佳答案

有关同一问题的其他问题,请参见评论部分。 您问题的简短答案:不,将这些类放在单例中不是可以的。 DocumentBuilderFactory和DocumentBuilder都不保证是线程安全的。如果您有多个解析XML的线程,请确保每个线程都有其自己的DoumentBuilder版本。每个线程只需要其中一个,因为您可以在重置文档构建器后重用它。

编辑一个小片段,表明使用相同的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实现-则不是。

07-28 13:07