final Multimap<Term, BooleanClause> terms = getTerms(bq);
for (Term t : terms.keySet()) {
Collection<BooleanClause> C = new HashSet(terms.get(t));
if (!C.isEmpty()) {
for (Iterator<BooleanClause> it = C.iterator(); it.hasNext();) {
BooleanClause c = it.next();
if(c.isSomething()) C.remove(c);
}
}
}
SSCCEではありませんが、においを拾うことができますか?
Iterator
以下のためのHashSet
クラスは、フェイルファスト反復子です。HashSet
クラスのドキュメントから:
このクラスのイテレータメソッドによって返されるイテレータはフェイルファストです。イテレータが作成された後、イテレータ自身のremoveメソッド以外の方法でセットが変更されると、イテレータはConcurrentModificationExceptionをスローします。したがって、同時変更に直面した場合、イテレーターは、将来の不確定な時点で任意の非決定論的な動作のリスクを冒すのではなく、迅速かつクリーンに失敗します。
イテレータのフェイルファスト動作は、一般的に言えば、非同期の同時変更が存在する場合にハードな保証を行うことが不可能であるため、保証できないことに注意してください。フェイルファーストイテレータは、ベストエフォートベースでConcurrentModificationExceptionをスローします。したがって、この例外に依存して正確であるプログラムを作成するのは誤りです。反復子のフェイルファスト動作は、バグを検出するためだけに使用する必要があります。
最後の文に注意してください-キャッチしているという事実ConcurrentModificationException
は、別のスレッドがコレクションを変更していることを意味します。同じJavadoc APIページにも次のように記載されています。
複数のスレッドがハッシュセットに同時にアクセスし、少なくとも1つのスレッドがセットを変更する場合は、外部で同期する必要があります。これは通常、セットを自然にカプセル化するいくつかのオブジェクトで同期することによって行われます。そのようなオブジェクトが存在しない場合は、Collections.synchronizedSetメソッドを使用してセットを「ラップ」する必要があります。これは、セットへの偶発的な非同期アクセスを防ぐために、作成時に行うのが最適です。
Set s = Collections.synchronizedSet(new HashSet(...));
Javadocへの参照は、次に何をすべきかについて自明であると思います。
さらに、あなたの場合、オブジェクトにImmutableSet
HashSetを作成する代わりに、なぜそれを使用していないのかわかりませんterms
(これは、途中で変更される可能性があります。getTerms
メソッドの実装を確認することはできませんが、基になるキーセットが変更されています)。不変セットを作成すると、現在のスレッドが元のキーセットの独自の防御コピーを持つことができます。
ConcurrentModificationException
(Java APIドキュメントに記載されているように)同期セットを使用することで防止できますが、すべてのスレッドが同期コレクションにアクセスし、バッキングコレクションに直接アクセスしないことが前提条件であることに注意してください(これは、HashSet
の基になるコレクションMultiMap
は他のスレッドによって変更されますが、おそらく1つのスレッドで作成されます。同期されたコレクションクラスは、スレッドがアクセスを取得するための内部ミューテックスを実際に維持します。他のスレッドから直接mutexにアクセスすることはできないため(そして、ここではそうするのはかなり馬鹿げているでしょう)、クラスのメソッドを使用して、キーセットまたはMultiMap自体の防御コピーを使用することを検討する必要があります。unmodifiableMultimap
MultiMaps
(getTermsメソッドから変更不可能なMultiMapを返す必要があります)。同期されたMultiMapを返す必要性を調査することもできますが、その場合も、基礎となるコレクションを同時変更から保護するために、任意のスレッドがミューテックスを取得する必要があることを確認する必要があります。
実際のコレクションへの同時アクセスが保証されるかどうかがわからないという唯一の理由で、スレッドセーフのHashSet
使用について言及することを意図的に省略しています。ほとんどの場合そうではありません。
編集:ConcurrentModificationException
s Iterator.next
シングルスレッドシナリオでスローされます
これはif(c.isSomething()) C.remove(c);
、編集された質問で導入されたステートメントに関するものです。
呼び出しCollection.remove
によって質問の性質が変わりConcurrentModificationException
ます。シングルスレッドのシナリオでもsをスローできるようになったためです。
可能性は、Collection
の反復子の使用と組み合わせてメソッド自体を使用することから発生します。この場合it
、ステートメントを使用して初期化された変数ですIterator<BooleanClause> it = C.iterator();
。
Iterator
it
その繰り返し処理オーバーCollection
C
の現在の状態に保存状態の関連Collection
。この特定のケース(Sun / Oracle JREを想定)では、KeyIterator
(HashMap
で使用されるクラスの内部内部クラスHashSet
)を使用してを反復処理しますCollection
。これの特定の特徴Iterator
は、そのメソッドを介してCollection
(HashMap
この場合)で実行された構造変更の数を追跡することIterator.remove
です。
を直接呼び出しremove
たCollection
後、の呼び出しで追跡するとIterator.next
、反復子はをスローし、が認識していないの構造変更が発生したかどうかConcurrentModificationException
をIterator.next
確認します。この場合は、によって追跡されますがによって追跡されない構造変更が発生します。Collection
Iterator
Collection.remove
Collection
Iterator
問題のこの部分を克服するには、ではなくを呼び出す必要がIterator.remove
あります。Collection.remove
これにより、Iterator
がへの変更を認識できるようになりCollection
ます。Iterator
この場合には、を介して発生する構造的な変更追跡するremove
方法。したがって、コードは次のようになります。
final Multimap<Term, BooleanClause> terms = getTerms(bq);
for (Term t : terms.keySet()) {
Collection<BooleanClause> C = new HashSet(terms.get(t));
if (!C.isEmpty()) {
for (Iterator<BooleanClause> it = C.iterator(); it.hasNext();) {
BooleanClause c = it.next();
if(c.isSomething()) it.remove(); // <-- invoke remove on the Iterator. Removes the element returned by it.next.
}
}
}
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加