【发布时间】:2014-08-17 09:34:52
【问题描述】:
首先:我知道 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());
}
保存操作是通过使用XMLEncoder 将List 保存到一个xml 文件中来完成的。它看起来像这样:
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);
}
}
这一切都很好。现在我想向父对象添加一些子对象,只需将新子对象添加到子列表并重新运行saveParents()。这是我的麻烦开始的地方:
如果我在父级中使用ArrayList 来保存子级,如下所示:
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(...) 工作将是最优雅的方式)
【问题讨论】:
-
以防万一有人遇到类似问题。我无法让另一种数据类型正常工作,也没有时间缩小原因。必须是涉及的框架之一,但它们是封闭源代码,所以那里没有运气。但是:我在所有将使用同步化更改数组列表的方法上实现了实例范围的锁定。甚至认为这不会很好地扩展,对于我当前的项目来说,它已经足够好了,直到我为产生错误的框架找到一些开源替代品。
标签: java multithreading serialization arraylist xmlencoder