10

私が取り組んでいるマルチスレッド アプリケーションでは、ときどきConcurrentModificationExceptionsリスト (主に 、場合によってはArrayListベクトル) が表示されます。しかし、コレクションを繰り返し処理すると項目が欠落しているように見えるが、例外がスローされないため、同時変更が発生していると思われる場合もあります。ConcurrentModificationExceptionのドキュメントには信頼できないと書かれていることは知っていますが、リストを同時に変更していないことを確認するにはどうすればよいですか? また、コレクションへのすべてのアクセスを同期ブロックでラップすることが、それを防ぐ唯一の方法ですか?

更新:はい、私は について知ってCollections.synchronizedCollectionいますが、コレクションを反復している間に誰かがコレクションを変更するのを防ぎません。コレクションを繰り返し処理しているときに誰かがコレクションに何かを追加すると、少なくとも私の問題の一部が発生していると思います。

2 番目の更新 Jason が行ったように、synchronizedCollection とクローン作成の言及を、java.util.concurrent と jacekfoo や Javamann のような apache コレクション フレームワークの言及と組み合わせたい場合、私は答えを受け入れることができます。

4

12 に答える 12

6

更新頻度にもよりますが、私のお気に入りの 1 つは CopyOnWriteArrayList または CopyOnWriteArraySet です。同時変更の例外を回避するために、更新時に新しいリスト/セットを作成します。

于 2008-09-16T18:05:56.417 に答える
4

あなたの元の質問は、スレッドセーフを維持しながら、基になるコレクションのライブ更新を確認するイテレーターを求めているようです。これは、一般的なケースで解決するには非常にコストのかかる問題です。そのため、標準のコレクションクラスでは解決できません。

問題の部分的な解決策を実現する方法はたくさんありますが、アプリケーションでは、そのうちの1つで十分な場合があります。

Jasonは、スレッドセーフを実現し、ConcurrentModificationExceptionのスローを回避するための特定の方法を提供しますが、これは活気を犠牲にするだけです。

Javamannは、スケーラビリティが重要であるロックフリーの方法で同じ問題を解決するパッケージ内の2つの特定のクラスについて言及しています。java.util.concurrentこれらはJava5にのみ付属していますが、パッケージの機能を以前のJavaバージョンにバックポートするさまざまなプロジェクトがありますが、これには、以前のJREではそれほど優れたパフォーマンスはありません。

すでにいくつかのApacheCommonsライブラリを使用している場合、jacekfooが指摘しているように、apacheコレクションフレームワークにはいくつかの役立つクラスが含まれています。

また、 Googleコレクションフレームワークを検討することも検討してください。

于 2008-09-17T11:04:26.763 に答える
3

並行性をより適切に処理するように設計された標準コレクション クラスのバージョンについては、java.util.concurrentを確認してください。

于 2008-09-16T17:51:57.260 に答える
3

はい、コレクション オブジェクトへのアクセスを同期する必要があります。

または、既存のオブジェクトに対して同期ラッパーを使用することもできます。Collections.synchronizedCollection()を参照してください。例えば:

List<String> safeList = Collections.synchronizedList( originalList );

ただし、すべてのコードは安全なバージョンを使用する必要があり、別のスレッドが変更している間に反復すると問題が発生します。

反復の問題を解決するには、最初にリストをコピーします。例:

for ( String el : safeList.clone() )
{ ... }

より最適化されたスレッドセーフなコレクションについては、java.util.concurrentも参照してください。

于 2008-09-16T17:52:40.787 に答える
2

通常、反復中にリストから要素を削除しようとすると、 ConcurrentModificationException が発生します。

これをテストする最も簡単な方法は次のとおりです。

List<Blah> list = new ArrayList<Blah>();
for (Blah blah : list) {
     list.remove(blah); // will throw the exception
}

どうやってそれを回避するのかわかりません。独自のスレッド セーフ リストを実装する必要がある場合もあれば、書き込み用に元のリストのコピーを作成し、リストに書き込む同期クラスを作成することもできます。

于 2008-09-16T17:54:17.677 に答える
1

実装を参照してください。基本的にintを格納します:

transient volatile int modCount;

そして、「構造変更」(削除など)があると増加します。modCount が変更されたことを反復子が検出すると、同時変更例外がスローされます。

同期 (Collections.synchronizedXXX 経由) は、イテレータの安全性を保証しないため、うまくいきません。put、get、set ... 経由で書き込みと読み取りを同期するだけです。

java.util.concurennt および apache コレクション フレームワークを参照してください (最適化されたいくつかのクラスは、書き込みよりも読み取り (同期されていない) が多い場合に同時実行環境で正しく動作します - FastHashMap を参照してください)。

于 2008-09-16T18:06:33.217 に答える
1

防御コピーを使用して、1 つの変更がList他の変更に影響を与えないようにすることができます。

于 2008-09-16T17:52:09.397 に答える
1

コレクションへのアクセスを同期ブロックでラップするのが、これを行う正しい方法です。標準的なプログラミング手法では、複数のスレッド間で共有される状態を処理するときに、ある種のロック メカニズム (セマフォ、ミューテックスなど) を使用する必要があります。

ただし、ユースケースによっては、通常、特定のケースでのみロックするように最適化を行うことができます。たとえば、頻繁に読み取られるがほとんど書き込まれないコレクションがある場合、同時読み取りを許可し、書き込みが進行中の場合は常にロックを適用できます。同時読み取りは、コレクションが変更中の場合にのみ競合を引き起こします。

于 2008-09-16T17:55:49.840 に答える
1

あなたが求めているのは難しい問題であるため、 ConcurrentModificationException はベストエフォートです。アクセスパターンがリストを同時に変更しないことを証明する以外に、パフォーマンスを犠牲にすることなくこれを確実に行う良い方法はありません。

同期は同時変更を防ぐ可能性が高く、最終的にはそれが頼りになるかもしれませんが、コストがかかる可能性があります. 最善の方法は、おそらく座って、アルゴリズムについてしばらく考えることです。ロックのない解決策が思いつかない場合は、同期に頼ってください。

于 2008-09-16T17:56:21.573 に答える
1

リスト全体で繰り返し同期することもできます。

List<String> safeList = Collections.synchronizedList( originalList );

public void doSomething() {
   synchronized(safeList){
     for(String s : safeList){
           System.out.println(s);

     }
   }

}

これにより、同期時にリストがロックされ、リストの編集中または反復中にリストにアクセスしようとするすべてのスレッドがブロックされます。欠点は、ボトルネックを作成することです。

これにより、 .clone() メソッドよりもメモリが節約され、反復で何をしているかによっては高速になる可能性があります...

于 2009-04-28T06:41:48.207 に答える
-1

Collections.synchronizedList()はリストを名目上スレッドセーフにレンダリングし、java.util.concurrent はより強力な機能を備えています。

于 2008-09-16T17:52:57.883 に答える
-1

これにより、同時変更の例外が取り除かれます。ただし、効率については説明しません;)

List<Blah> list = fillMyList();
List<Blah> temp = new ArrayList<Blah>();
for (Blah blah : list) {
     //list.remove(blah);  would throw the exception
     temp.add(blah);
}
list.removeAll(temp);
于 2008-09-16T20:58:17.453 に答える