Thread Safety in Java XML Parser Factories
This article provides a direct overview of the thread-safety
guarantees for Java’s core XML processing factories, specifically
DocumentBuilderFactory and SAXParserFactory.
According to the official Java API for XML Processing (JAXP)
specification, neither these factories nor the parser instances they
produce are thread-safe. Below is a detailed breakdown of these
constraints and the standard design patterns required to handle XML
parsing safely in multithreaded applications.
Thread Safety of the Factories
Neither DocumentBuilderFactory nor
SAXParserFactory is guaranteed to be thread-safe by the
Java SE specification.
- Configuration Methods: Modifying factory settings
(such as calling
setNamespaceAware(boolean),setValidating(boolean), orsetFeature(String, boolean)) while another thread is reading from or using the factory results in undefined behavior and race conditions. - Instance Creation: Even after configuration is
complete, calling
newDocumentBuilder()ornewSAXParser()concurrently across multiple threads on a shared factory instance is not guaranteed to be safe across all underlying JAXP implementations (e.g., Apache Xerces).
The official specification explicitly states that it is the application’s responsibility to ensure that a single instance of a factory is accessed by only one thread at any given time, or that access is explicitly synchronized.
Thread Safety of the Parsers
The instances produced by these
factories—DocumentBuilder and SAXParser—are
strictly not thread-safe.
- An individual
DocumentBuilderorSAXParserinstance cannot be shared across threads. - A single parser instance cannot parse multiple XML documents concurrently.
- Calling
parse()concurrently on the same parser instance will corrupt internal state, leading to parsing errors, incomplete DOM trees, or runtime exceptions. - While instances can be reused sequentially by calling
reset(), sequential reuse must still remain confined to a single thread or protected by synchronization.
Recommended Concurrency Patterns
To safely process XML in a concurrent environment, use one of the following approaches:
1. Instantiate Per Use
The simplest approach is to create a new factory and parser within the scope of the method handling the XML document.
public Document parseXml(InputStream input) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Configure features
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(input);
}Trade-off: Safe and simple, but incurs the performance overhead of repeated Service Provider Interface (SPI) lookups and factory instantiation on high-throughput systems.
2. ThreadLocal Storage
To avoid repeated instantiation costs while maintaining thread
safety, store configured parser or factory instances in a
ThreadLocal.
public class XmlParserHolder {
private static final ThreadLocal<DocumentBuilder> BUILDER_HOLDER = ThreadLocal.withInitial(() -> {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
return factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
});
public static Document parse(InputStream is) throws Exception {
DocumentBuilder builder = BUILDER_HOLDER.get();
builder.reset(); // Clear state before reuse
return builder.parse(is);
}
}Trade-off: High performance, but requires proper lifecycle management in managed thread pools to prevent memory leaks and state carryover between requests.
3. Object Pooling
For high-concurrency environments with strict memory constraints,
maintain a bounded pool of pre-configured DocumentBuilder
or SAXParser instances using a pool manager. Threads borrow
a parser, call reset(), execute the parse operation, and
return the parser to the pool upon completion.