我计划在多个线程可以查询文档的应用程序中使用dom4j DOM Document作为静态缓存。 考虑到文档本身永远不会改变的帐户,从多个线程查询它是否安全?
我编写了以下代码来测试它,但我不确定它确实证明操作是安全的吗?
package test.concurrent_dom;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
/**
* Hello world!
*
*/
public class App extends Thread
{
private static final String xml =
"<Session>"
+ "<child1 attribute1=\"attribute1value\" attribute2=\"attribute2value\">"
+ "ChildText1</child1>"
+ "<child2 attribute1=\"attribute1value\" attribute2=\"attribute2value\">"
+ "ChildText2</child2>"
+ "<child3 attribute1=\"attribute1value\" attribute2=\"attribute2value\">"
+ "ChildText3</child3>"
+ "</Session>";
private static Document document;
private static Element root;
public static void main( String[] args ) throws DocumentException
{
document = DocumentHelper.parseText(xml);
root = document.getRootElement();
Thread t1 = new Thread(){
public void run(){
while(true){
try {
sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Node n1 = root.selectSingleNode("/Session/child1");
if(!n1.getText().equals("ChildText1")){
System.out.println("WRONG!");
}
}
}
};
Thread t2 = new Thread(){
public void run(){
while(true){
try {
sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Node n1 = root.selectSingleNode("/Session/child2");
if(!n1.getText().equals("ChildText2")){
System.out.println("WRONG!");
}
}
}
};
Thread t3 = new Thread(){
public void run(){
while(true){
try {
sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Node n1 = root.selectSingleNode("/Session/child3");
if(!n1.getText().equals("ChildText3")){
System.out.println("WRONG!");
}
}
}
};
t1.start();
t2.start();
t3.start();
System.out.println( "Hello World!" );
}
}
答案 0 :(得分:5)
http://xerces.apache.org/xerces2-j/faq-dom.html说
没有。 DOM不需要 实现是线程安全的。如果 你需要从中访问DOM 你需要多个线程 添加适当的锁定到您的 应用程序代码。
如果没有看到实现,就不可能知道selectSingleNode
是否使用任何共享状态来读取DOM。我认为最安全的做法是假设它不是线程安全的。
另一种方法是使用自己的XPath处理器,例如Jaxen,它是线程安全的。
XPath对象是完全可重入的 线程安全的。它们不包含内部 评估的状态,因此可以 缓存缓存并在内部共享 应用。一旦你有XPath 对象,你可以应用它 各种初始上下文和检索 导致几种不同的方式: --- Introduction to SAX path and Jaxen
JAXEN Jira针对线程安全问题提供了各种修复,提供了Jaxen设计为线程安全的证据。这是one我偶然遇到的。 并且confirmation Jaxen对其中一位作者来说是线程安全的。
除了线程安全之外,Jaxen与模型无关 - 它适用于许多模型(W3C DOM,XOM,Dom4J,JDOM),并且可以通过实现几个接口来插入自定义模型。
我认为W3C DOM上的简单访问器和迭代器是线程安全的。但这只是一种预感,而不是一个具体的事实。如果您想100%确定,那么请使用专为线程安全设计的DOM,例如dom4j。
一些入门资源: - An example of using Jaxen。 - Jaxen FAQ和homepage
答案 1 :(得分:0)
我实际上并不熟悉dom4j DOM,但如果你不确定它是否可以正确处理只读数据,我不确定它有多好。
我将做出操作假设,即runnables的可执行部分(睡眠后的部分)花费不到1微秒,并且在测试运行中它们连续发生,而不是同时发生。因此,你的测试并没有真正证明什么。
对于更强大的测试,我
添加了原始冲突检测
final AtomicReference<Thread>owner=new AtomicReference<Thread>() ;
class TestThread
{
private String url ;
private String expected ;
TestThread(int index) { url = "/Session/child" + i ; expected="ChildText" + i ; }
public String toString () {return expected;}
public void run()
{
while(true)
{
boolean own=owner.compareAndSet(null,this);
Node n1 = root.selectSingleNode(url);
boolean wrong = !n1.getText().equals(result);
owner.compareAndSet(this,null);
if(!own) { System.out.println ( owner.get() + " conflicts " + this ) }
if(wrong){System.out.println(this + " WRONG!");
}
}
}
}
然后
try{
while(true){
Thread t1 = new TestThread(1);
t1.start();
Thread t2 = new TestThread(2);
t2.start();
Thread t3 = new TestThread(3);
t3.start();
}
}
catch(Throwable thr){
thr.printStackTrace();
}
如果它按预期工作(这是未编译和未经测试的),那么它将继续生成新线程,新线程将尝试读取文档。他们将报告他们是否可能与另一个线程发生冲突。如果他们读错了值,他们会报告。它将继续生成新线程,直到您的系统资源耗尽,然后它将崩溃。