DOM、SAX、JAXB和StAX的线程安全各不相同,DocumentBuilderFactory、SAXParserFactory、DOM节点、Marshaller、Unmarshaller及XMLStreamReader/Writer均非线程安全,需每线程独立实例或同步访问,仅JAXBContext和工厂类可共享,推荐初始化后固定配置并避免跨线程修改。
在处理XML解析或生成时,线程安全问题常常被忽视,尤其是在多线程环境下共享XML处理器或解析器实例的情况下。Java中常见的XML处理API(如DOM、SAX、StAX、JAXB)在设计上对线程安全的支持各不相同,使用不当可能导致数据错乱、异常抛出甚至程序崩溃。
javax.xml.parsers 包中的 DocumentBuilderFactory 和 SAXParserFactory 实例本身不是线程安全的。虽然工厂类通常被设计为可多次调用以创建新实例,但它们的内部配置(如设置属性、特征)若在多个线程中同时修改,会导致不可预知的行为。
建议:
org.w3c.dom.Document 及其相关节点对象(Element、Text等)在标准实现中(如Oracle JDK的com.sun.org.apache.xerces.internal)不支持并发读写。多个线程同时修改同一个DOM树会引发状态不一致或 ConcurrentModificationException。
如果需要多线程操作XML数据:
javax.xml.bind.JAXBContext 是线程安全的,可以被多个线程共享。它是重量级对象,通常建议在整个应用中只创建一次。
但 Marshaller 和 Unmarshaller 实例不是线程安全的。每个线程应使用自己的实例,或通过ThreadLocal管理。
示例做法:
private static final JAXBContext jaxbContext = JAXBContext.newInstance(MyClass.class); private static final ThreadLocalmarshallerThreadLocal = ThreadLocal.withInitial(() -> { try { return jaxbContext.createMarshaller(); } catch (JAXBException e) { throw new RuntimeException(e); } });
这样每个线程获取独立的 Marshaller,避免冲突。
XMLInputFactory 和 XMLOutputFactory 通常可共享,但生成的 XMLStreamReader 和 XMLStreamWriter 实例不能在多个线程间共享。
这些流式读写器维护内部状态,跨线程使用会导致解析错误或数据损坏。
正确做法:
基本上就这些。关键点是:大多数XML处理组件都不是为并发设计的,共享实例需格外小心。优先选择“每线程一实例”策略,配合不可变数据或同步控制,才能确保线程安全。