まず、SOにはこれに関する関連記事がたくさんあることは知っていますが、私の場合は見つけたものが役に立ちました。
だから、私がやっていることは、複数の子オブジェクトを持つことができる非常に単純な親オブジェクトを得ることです。どちらのオブジェクトも、Java Bean 仕様 (引数なしのコンストラクター、すべての変数のセッターおよびゲッター) に準拠しています。
複数の親が存在する場合があり、これらは次のようなリポジトリ クラスに保存されます。
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(...)
を機能させることが最もエレガントな方法です)