首先:我知道 SO 中有很多相关的帖子,但我能找到的帖子对我的情况有所帮助。
所以,我正在做的是我有一个非常简单的父对象,它可能有多个子对象。这两个对象都符合 java beans 规范(所有变量的无参数构造函数、setter 和 getter)。
可能有多个父级,它们保存在一个存储库类中,如下所示:
private final Map<String, Parent> parentItems = new ConcurrentHashMap<String, Parent>();
每次创建新的父级时,存储库类都会保存从列表中构建的父级列表,parentItems
如下所示:
public List<Parent> getParents() {
return new ArrayList<Parent>(parentItems.values());
}
保存操作是通过使用 将 保存List
到 xml 文件中来完成的XMLEncoder
。它看起来像这样:
public void saveParents(OutputStream os) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
XMLEncoder encoder = null;
try {
Thread.currentThread().setContextClassLoader(Parent.class.getClassLoader());
encoder = new XMLEncoder(os);
encoder.writeObject(getParents());
encoder.flush();
} finally {
if (encoder != null) {
encoder.close();
}
Thread.currentThread().setContextClassLoader(cl);
}
}
这一切都很好。现在我想向父对象添加一些子对象,然后将新的子对象添加到子 List 并重新运行saveParents()
。这是我的麻烦开始的地方:
如果我ArrayList
在父级中使用 a 来保存子级,如下所示:
private ArrayList<Child> children = new ArrayList<Child>();
一切正常。对象将保存在 xml 文件的父级中。它看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
<object class="java.util.ArrayList">
<void method="add">
<object class="org.mypackage.Parent" id="Parent0">
<void property="children">
<void method="add">
<object class="org.mypackage.Child">
<void property="displayName">
<string>Child1</string>
</void>
<void property="id">
<string>myid1</string>
</void>
<void property="parent">
<object idref="Parent0"/>
</void>
</object>
</void>
<void method="add">
<object class="org.mypackage.Child">
<void property="displayName">
<string>Child2</string>
</void>
<void property="id">
<string>myid2</string>
</void>
<void property="parent">
<object idref="Parent0"/>
</void>
</object>
</void>
</void>
<void property="displayName">
<string>Parent1</string>
</void>
<void property="id">
<string>myid</string>
</void>
</object>
</void>
</object>
</java>
但是:我们知道这ArrayList
不是线程安全的,而且我知道父对象可能会被多人更改。那么如何解决呢?使用synchronizedList(...)
- 对吗?就像是 ...
private List<Child> children = Collections.synchronizedList(new ArrayList<Child>());
...然后只需快速将 getter 和 setter 更改为List
而不是,ArrayList
我们应该没问题。好的,这应该可行 - 但猜猜看:它失败了:-(
每次我添加一个子对象(并在saveParent()
此之后运行)时,我都会得到一个StackOverflowException
. 如果我查看 XML,我会看到如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
<void class="java.util.ArrayList">
<void method="add">
<object class="org.mypackage.Child" id="Child0">
<void property="displayName">
<string>Child1</string>
</void>
<void property="id">
<string>myid</string>
</void>
<void property="parent">
<object class="org.mypackage.Parent"/>
</void>
</object>
</void>
</void>
<void class="java.util.ArrayList">
<void method="add">
<object idref="Child0"/>
</void>
</void>
<void class="java.util.ArrayList">
<void method="add">
<object idref="Child0"/>
</void>
</void>
<void class="java.util.ArrayList">
<void method="add">
<object idref="Child0"/>
</void>
</void>
...... AND THIS GOES ON AND ON AND ON .......
好吧,我猜我知道堆栈溢出是从哪里来的……但是为什么呢?它应该是可序列化的,但好吧,看看它......我如何获得一个List
不会爆炸的可序列化、线程安全的(或任何其他适合的数据类型)?也许您可以考虑任何解决方法以使其ArrayList
(在这种情况下有效)线程安全?也许通过使整个父对象线程安全?(但是:这可能有其他副作用,这就是为什么简单地让它synchronizedList(...)
工作将是最优雅的方式)