Java コレクションの変更不可能なラッパーは、それらをスレッドセーフにしますか?
-
01-07-2019 - |
質問
ArrayList の ArrayList をスレッドセーフにする必要があります。また、クライアントにコレクションを変更させることもできません。変更不可能なラッパーはスレッドセーフになりますか、それともコレクションに 2 つのラッパーが必要ですか?
解決
場合によります。ラッパーは、ラップするコレクションへの変更のみを防止し、コレクション内のオブジェクトへの変更は防止しません。ArrayList の ArrayList がある場合は、グローバル List とその各要素 List を個別にラップする必要があり、また、それらのリストの内容に対して何かを行う必要がある場合もあります。最後に、元のリスト オブジェクトが変更されていないことを確認する必要があります。これは、ラッパーはラッパー参照による変更のみを防止し、元のオブジェクトへの変更は防止しないためです。
この場合、同期ラッパーは必要ありません。
他のヒント
関連するトピックについては、スレッドの安全性を実現するために同期コレクションの使用を提案する返信をいくつか見ました。同期バージョンのコレクションを使用しても「スレッド セーフ」にはなりません。2 つの操作を組み合わせる場合、各操作 (挿入、カウントなど) はミューテックスによって保護されますが、それらがアトミックに実行されるという保証はありません。たとえば、次のコードは (同期キューを使用しても) スレッド セーフではありません。
if(queue.Count > 0)
{
queue.Add(...);
}
変更不可能なラッパーは、適用されるリストの構造の変更を防ぐだけです。このリストに他のリストが含まれており、これらのネストされたリストを変更しようとするスレッドがある場合、同時変更のリスクから保護されません。
Collections ソースを見ると、Unmodifiable がそうであるように見えます。 ない 同期させます。
static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
implements Set<E>, Serializable;
static class UnmodifiableCollection<E> implements Collection<E>, Serializable;
同期クラス ラッパーには、同期部分を実行するためのミューテックス オブジェクトが含まれているため、両方を取得するには両方を使用する必要があるようです。または、自分で巻きましょう!
UnmodifiableList ラッパーは ArrayList を最終フィールドに保存するため、ラッパーの作成後にリストが変更されない限り、ラッパーの読み取りメソッドはラッパーの作成時と同じようにリストを参照すると思います。ラッパー内の変更可能な ArrayList が変更されない限り (ラッパーはこれを防ぐことができません)。
変更不可能なビューが安全に公開され、変更不可能なビューの公開後、変更可能なオリジナル (コレクションに再帰的に含まれるすべてのオブジェクトを含む) が決して変更されない場合、スレッド セーフになります。
オリジナルを変更し続けたい場合は、コレクションのオブジェクト グラフの防御的なコピーを作成してその変更不可能なビューを返すか、最初から本質的にスレッドセーフなリストを使用して、変更不可能なビューを返すことができます。それ。
あなた できない その後も非同期でリストにアクセスする場合は、unmodifiableList(synchonizedList(theList)) を返します。可変状態が複数のスレッド間で共有される場合、 全て スレッドは 同じ その状態にアクセスするとロックされます。
不変オブジェクトは定義上、スレッド セーフであるため (元のコレクションへの参照を誰も保持しないと仮定して)、同期は ない 必要。
collections.unmodifiablelist()を使用して外側のアレイリストをラップすると、クライアントがコンテンツを変更するのを防ぎます(したがって、スレッドが安全になります)が、内側の配列リストはまだ変化します。
collections.unmodifiablelist()を使用して内側の配列リストをラップすると、クライアントがコンテンツを変更する(したがってスレッドが安全になる)ことを防ぎます。これは必要なものです。
この解決策で問題 (オーバーヘッド、メモリ使用量など) が発生する場合はお知らせください。他の解決策があなたの問題に適用できるかもしれません。:)
編集:もちろん、リストが変更された場合、それらはスレッドセーフではありません。これ以上の編集は行われないと考えていました。
あなたが何をしようとしているのか理解できたかどうかわかりませんが、ほとんどの場合、答えは「いいえ」だと思います。
ArrayList とその両方の ArrayList を設定した場合、外側のリストと内側のリストは作成後に変更することはできません (また、作成中は 1 つのスレッドのみが内側のリストと外側のリストのいずれかにアクセスできます)。これらはおそらくラッパーによってスレッド セーフです (両方のリストが存在する場合)。 、外部リストと内部リストは、変更が不可能な方法でラップされています)。ArrayList に対するすべての読み取り専用操作は、おそらくスレッドセーフです。ただし、Sun はそうではありません。 保証 それらはスレッドセーフであるため (読み取り専用操作に対してもそうではありません)、そのため、現時点では機能するとしても、将来的には機能しなくなる可能性があります (たとえば、Sun がより迅速なアクセスのためにデータの内部キャッシュを作成した場合)。
これは次の場合に必要です。
- 元の変更可能なリストへの参照がまだ残っています。
- リストはイテレータを介してアクセスされる可能性があります。
ArrayList からインデックスのみで読み取る場合は、これがスレッドセーフであると想定できます。
迷った場合は、同期ラッパーを選択してください。