3

我有一个进行 xml 解析的函数。我想让函数线程安全,但也要尽可能优化(减少阻塞)。
简而言之,代码如下:

public Document doXML(InputStream s)
{
//Some processing.
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  DocumentBuilder parser = factory.newDocumentBuilder();
  Document xmlDoc = parser.parse(is);
  return xmlDoc;

}

但我不想在每次调用中创建一个新的 DocumentBuilderFactory 或 DocumentBuilder。
我想重用工厂和解析器,但我不确定它们是线程安全的。那么最优化的方法是什么?
1)在类字段中缓存一个DocumentBuilderFactory,并同步factory.newDocumentBuilder();这样每个线程都有自己的 DocumentBuilder 实例
2)缓存 DocumentBuilderFactoryDocumentBuilder 并同步 parser.parse(is); 每个线程
我认为(2)是最好的,但是这样做安全吗?我也可以通过同步来避免阻塞吗?我希望它尽可能快。

谢谢?

4

4 回答 4

5

如果您正在重用线程(如在线程池中),您可以将 DocumentBuilderFactory 声明为线程本地的。为每个线程创建一个新集合会产生开销,但正如我所说,如果您要重用,那么后续开销非常低。

final ThreadLocal<DocumentBuilderFactory> documentBuilderFactor = new ThreadLocal<DocumentBuilderFactory>(){
     public DocumentBuilderFactory  initialValue(){
       return  DocumentBuilderFactory.newInstance();
     }
}

public Document doXML(InputStream s)
{
//Some processing.
  DocumentBuilderFactory factory = documentBuilderFactor.get();
  DocumentBuilder parser = factory.newDocumentBuilder();
  Document xmlDoc = parser.parse(is);
  return xmlDoc;

}

在这里,您将只为每个线程创建一个 DocumentBuilderFactory。

我不知道解析时 DocumentBuilder 是否是线程安全的(它是不可变的吗?)。但是,如果 DocumentBuilder 在解析时是线程安全的,那么您可以使用我所说的相同机制。

该分辨率将使整体吞吐量尽可能快。

注意:这没有经过测试或编译,只是对我所指的内容有所了解。

于 2010-11-18T17:13:19.303 回答
2

2) 将是线程安全的,但您的应用程序一次只能解析一个文档。

为什么不直接使用您拥有的代码?做

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

有明显不可接受的开销?

于 2010-11-18T17:01:27.430 回答
1

如果你想避免同步阻塞,你应该确保使用原子操作。的行为javax.xml.parser.*取决于实现(您可以使用系统属性指定实现,或调用实现代码)。根据线程数和每个线程的负载权重,控制解析器对象的创建可能是合理的。您应该在创建新解析器或等待解析器之间进行选择。代码可以在启动时创建解析器池,然后线程从池中获取解析器,当没有空闲解析器时阻塞。一旦线程获得解析器,它就会解析数据,重置解析器并放回池中。您始终可以通过池的长度来控制时间/内存使用情况。

于 2010-11-18T16:59:06.287 回答
1

我在类似的情况下遇到了一些性能问题。我在每次使用时创建工厂对象以避免线程问题(每秒 10 个)。那个(诚然旧的)平台中的 XML 实现为服务提供者类做了一些相对较慢的查找逻辑。

我的调整是确定结果并通过命令行属性对其进行配置。这导致查找被跳过。

-Djavax.xml.parsers.DocumentBuilderFactory=com.example.FactoryClassName
-Djavax.xml.transform.TransformerFactory=com.example.OtherFactoryClassName

令人沮丧的是,如果找到一个类,查找代码具有缓存逻辑。但是没有缓存未命中(没有找到,使用默认值)。处理负面情况的稍微更好的查找缓存将使这变得不必要。

这还需要吗?需要在您的环境中进行测试。我在 Solaris 上使用了 truss 来注意到该查找逻辑导致的非常频繁的文件操作。

于 2010-11-18T18:15:59.103 回答