古い同期されたベクターコレクション、同期されたアクセスを持つArrayList、Collections.synchronizedList、または同時アクセスのための他のソリューションを使用する必要がありますか?
関連する質問にも検索にも私の質問が表示されません( コレクションをスレッドセーフにしますか? 同じではありません)。
最近、アプリケーションのGUI部分で一種の単体テストを行う必要がありました(基本的にAPIを使用してフレームを作成したり、オブジェクトを追加したりします)。これらの操作はユーザーよりもはるかに高速に呼び出されるため、まだ作成されていない、またはすでに削除されているリソースにアクセスしようとするメソッドに多くの問題があります。
EDTで発生する特定の問題は、別のスレッドでビューのリンクリストを変更しながらウォークすることから発生しました(他の問題の中でもConcurrentModificationExceptionが発生します)。単純な配列リストではなくリンクリストである理由を聞かないでください(一般に0または1のビューが含まれているため、さらに少なくなります...)。そのため、質問ではより一般的なArrayListを使用しました(古いいとこ)。
とにかく、並行性の問題にあまり精通していないので、私は少し情報を調べて、古い(そしておそらく時代遅れの)Vector(設計により同期された操作を持っている)、synchronized (myList) { }
を持つArrayListから何を選ぶべきか疑問に思いましたクリティカルセクションの周囲(追加/削除/ウォーク操作)またはCollections.synchronizedListによって返されるリストの使用(後者の使用方法さえわからない)。
もう1つの設計上の誤りは、オブジェクトを使用するメカニズムを提供する代わりに、オブジェクトを公開すること(getViewList()メソッド...)であったため、最終的に2番目のオプションを選択しました。
しかし、他のアプローチの長所と短所は何ですか?
[編集]ここにはたくさんの良いアドバイスがありますが、どれかを選ぶのは難しいです。私はより詳細で考えのためのリンク/食べ物を提供することを選択します... :-)私もダロンのものが好きです。
要約する:
私はそれを正しく理解したと思います... :-)
Collections.synchronizedList()によって返されるVectorとListは、道徳的に同じものです。 Vectorは事実上(実際にはそうではありませんが)非推奨であり、代わりに常に同期リストを好むと思います。唯一の例外は、Vectorを必要とする古いAPI(特にJDKのAPI)です。
ネイキッドArrayListを使用し、個別に同期することで、同期をより正確に調整する機会が得られます(相互に排他的なブロックに追加のアクションを含めるか、リストへの複数の呼び出しを1つのアトミックアクションにまとめます)。欠点は、同期の外部でネイキッドArrayListにアクセスするコードを記述できることです。これは壊れています。
検討したいもう1つのオプションは、CopyOnWriteArrayListです。これにより、Vectorや同期されたArrayListのようにスレッドセーフが得られますが、データの非ライブスナップショットで動作しているため、ConcurrentModificationExceptionをスローしないイテレーターも提供されます。
これらのトピックに関するこれらの最近のブログのいくつかは興味深いと思うかもしれません:
「 Java Concurrency inPractice "」という本を強くお勧めします。
それぞれの選択肢には長所/短所があります。
Vector
よりもArrayList
を好む正当な理由は考えられません。 Vector
のリスト操作は同期されます。つまり、複数のスレッドが安全に変更できます。そして、あなたが言うように、ArrayListの操作はCollections.synchronizedList
を使用して同期することができます。
同期リストを使用している場合でも、別のスレッドによって変更されているときにコレクションを反復処理すると、ConcurrentModificationExceptions
が発生することに注意してください。したがって、そのアクセスを外部で調整することが重要です(2番目のオプション)。
反復の問題を回避するために使用される一般的な手法の1つは、コレクションの不変のコピーを反復することです。 Collections.unmodifiableList
を参照してください
私は常にJava.util.concurrent( http://Java.Sun.com/javase/6/docs/api/Java/util/concurrent/package-summary.html )パッケージにアクセスしますまず、適切なコレクションがあるかどうかを確認します。 Java.util.concurrent.CopyOnWriteArrayListクラスは、リストにほとんど変更を加えず、反復回数を増やす場合に適しています。
また、VectorとCollections.synchronizedListがConcurrentModificationExceptionを防ぐとは思わない。
適切なコレクションが見つからない場合は、独自の同期を行う必要があります。反復するときにロックを保持したくない場合は、コピーを作成してコピーを反復することを検討してください。
Vectorによって返されるIterator
が同期されているとは思いません。つまり、Vector
は、1つのスレッドが基になるコレクションを変更していないことを(それ自体で)保証できません。別のスレッドがそれを繰り返している間。
反復がスレッドセーフであることを保証するには、同期を自分で処理する必要があると思います。同じVector/List/objectが複数のスレッドで共有されている(したがって問題が発生している)と仮定すると、そのオブジェクト自体で同期することはできませんか?
最も安全な解決策は、共有データへの同時アクセスを完全に回避することです。非EDTスレッドが同じデータを操作する代わりに、変更を実行するRunnableを使用してSwingUtilities.invokeLater()
を呼び出すようにします。
真剣に、共有データの同時実行性は、どこかに隠れている別の競合状態やデッドロックがないかどうかわからない毒蛇の巣であり、最悪の場合にあなたを尻に噛む時間を競います。
CopyOnWriteArrayListは一見の価値があります。これは、通常から読み取られるリスト用に設計されています。書き込むたびに、裏で新しい配列が作成されるため、配列全体で反復するものはConcurrentModificationExceptionを取得しません。