2015-08-20 59 views
0

我嘗試使用Saxon HE作爲內置JAXP實現的替代品,並且我遇到了各種線程問題。Saxon在重新使用DOM模板時沒有線程安全

出於性能方面的原因,我緩存模板作爲DOM文檔和重用他們跨線程這樣

 TransformerFactory factory = TransformerFactory.newInstance(); 
     Document template = getFromCache(...); 
     Transformer transformer = factory.newTransformer(new DOMSource(template)); 
     transformer.transform(new DOMSource(document), streamResult); 

我知道DOM是不能保證是線程安全的,但JAXP XSLT執行,同時撒克遜可以正常使用完全崩潰。怎麼會這樣?

有沒有辦法使其工作,但保持性能的緩存到位?

撒克遜例外發生在不同的地方,但最常見於:

java.lang.NullPointerException: null 
at com.sun.org.apache.xerces.internal.dom.ParentNode.nodeListItem(ParentNode.java:786) ~[na:1.7.0_71] 
at com.sun.org.apache.xerces.internal.dom.ParentNode.item(ParentNode.java:800) ~[na:1.7.0_71] 
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:154) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMSender.send(DOMSender.java:92) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.dom.DOMObjectModel.sendSource(DOMObjectModel.java:250) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.event.Sender.send(Sender.java:221) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.style.StylesheetModule.loadStylesheetModule(StylesheetModule.java:128) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:131) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:94) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:543) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.jaxp.SaxonTransformerFactory.newTemplates(SaxonTransformerFactory.java:152) ~[Saxon-HE-9.6.0-7.jar:na] 
at net.sf.saxon.jaxp.SaxonTransformerFactory.newTransformer(SaxonTransformerFactory.java:108) ~[Saxon-HE-9.6.0-7.jar:na] 
+0

你可以同步訪問特定的'文檔'嗎?如果一個DOM不能保證線程安全,那麼在多線程場景中使用它,而不保護對單個實例的訪問,則會導致不好的腳步。 –

+1

你的方法似乎是合理的,因爲TransformerFactory實現應該只需要讀取DOM來創建Transformer(除非它不)。你是什​​麼意思*徹底崩潰*:是否有任何異常拋出? – wero

+0

添加了最常出現的異常詳細信息... – rustyx

回答

1

不知道是什麼原因導致您所描述的錯誤,但javax.xml.transformTemplates類是重用樣式表的正式方法一種線程安全的方式,並避免爲每一次轉換創建和解析它們的開銷。

  1. 充分利用緩存存儲Templates,而不是DOM文檔的模板源,使用TransformerFactory.newTemplates(Source)
  2. 調用Templates.newTransformer()緩存模板對象以獲取單個轉換的Transformer。

這甚至應該比您當前的解決方案更快。


編輯:

的NPE似乎從Xerces的這樣一行字:發起:

fNodeListCache.fChildIndex = i; 

在我眼裏,這是一個強烈的線索,多線程訪問DOM導致錯誤而不是撒克遜人。不知道爲什麼默認的TransformerFactory存在這種情況。

1

您說:「出於性能方面的原因,我將模板緩存爲DOM文檔並在線程間重複使用它們。但這根本無助於表現。它僅爲您節省I/O和XML解析成本,這只是編譯樣式表成本的一小部分。您應該緩存模板對象,這是樣式表的編譯表示。這不僅提供了更有效的緩存,而且它也恰好是線程安全的。

也許內置的JAXP實現可以通過進行自己的同步來彌補DOM中線程安全性的缺失。這當然是可能的,但這不是一個明智的用例,所以幾乎沒有必要。

你應該知道,如果你關心性能,就像你說的那樣,那麼以撒克遜的形式提供DOM形式的實例文檔效率非常低 - 通常比允許撒克遜使用它的速度慢5-10倍內部樹格式。

+0

我使用DOM API在Java中創建複雜的輸入文檔。 Saxon中是否有另一個API可以即時創建XML文檔並且不會導致Saxon的性能下降?我見過'TinyBuilder',但它是面向SAX的,因此在代碼中創建文檔不太方便... – rustyx

+0

與Saxon的內部樹模型相比,所有外部樹模型都會帶來懲罰;內部模型獲得了性能提升*,因爲它嚴重限制了更新樹的能力。如果您需要一種可變樹格式,也可以用作Saxon的輸入(高效),我會推薦JDOM2或XOM,它們比DOM更接近XDM數據模型。或者,在轉換之前將DOM複製到撒克遜樹(例如,在DOMSource上使用Configuration.buildDocument())有時更有效。或者,只需支付額外的費用。 –