マルチスレッド環境に非同期のJavaコレクションがあり、コレクションのリーダーを強制的に同期させたくない場合[1]、ライターを同期して参照割り当てのアトミック性を使用するソリューションは実行可能ですか?何かのようなもの:
private Collection global = new HashSet(); // start threading after this
void allUpdatesGoThroughHere(Object exampleOperand) {
// My hypothesis is that this prevents operations in the block being re-ordered
synchronized(global) {
Collection copy = new HashSet(global);
copy.remove(exampleOperand);
// Given my hypothesis, we should have a fully constructed object here. So a
// reader will either get the old or the new Collection, but never an
// inconsistent one.
global = copy;
}
}
// Do multithreaded reads here. All reads are done through a reference copy like:
// Collection copy = global;
// for (Object elm: copy) {...
// so the global reference being updated half way through should have no impact
このような状況では、独自のソリューションをロールすることは失敗することが多いようです。そのため、データコンシューマーのオブジェクトの作成とブロックを防ぐために使用できる他のパターン、コレクション、またはライブラリについて知りたいと思います。
[1]デッドロックが発生するリスクと相まって、書き込みに比べて読み取りに費やされる時間の割合が高い理由。
編集:いくつかの回答とコメントの多くの良い情報、いくつかの重要なポイント:
- 投稿したコードにバグがありました。グローバル(不適切な名前の変数)で同期すると、スワップ後に同期されたブロックを保護できない場合があります。
- クラスで同期する(synchronizedキーワードをメソッドに移動する)ことでこれを修正できますが、他のバグがある可能性があります。より安全で保守しやすい解決策は、java.util.concurrentの何かを使用することです。
- 私が投稿したコードには「結果整合性の保証」はありません。リーダーがライターによる更新を確認できるようにする1つの方法は、volatileキーワードを使用することです。
- 振り返ってみると、この質問の動機となった一般的な問題は、Javaでロックされた書き込みを使用してロックのない読み取りを実装しようとしたことでしたが、私の(解決された)問題はコレクションにあり、将来の読者を不必要に混乱させる可能性があります。したがって、私が投稿したコードが機能することが明らかでない場合は、一度に1人のライターが、複数のリーダースレッドによって保護されずに読み取られている「オブジェクト」の編集を実行できるようにします。編集のコミットはアトミック操作によって行われるため、読者は編集前または編集後の「オブジェクト」のみを取得できます。リーダースレッドが更新を取得した場合、「オブジェクト」の古いコピーで読み取りが行われているため、読み取りの途中で更新を行うことはできません。Javaでより優れた並行性サポートが利用可能になる前に、おそらく発見され、何らかの方法で壊れていることが証明された単純なソリューション。