12

EnumerationをスローしないConcurrentModificationExceptionのはなぜですか?

以下のコードを参照してください。

public static void main(String[] args) {

    Vector<String> v=new Vector<String>();
    v.add("Amit");
    v.add("Raj");
    v.add("Pathak");
    v.add("Sumit");
    v.add("Aron");
    v.add("Trek");

    Enumeration<String> en=v.elements();

    while(en.hasMoreElements())
    {
        String value=(String) en.nextElement();
        System.out.println(value);
        v.remove(value);

    }

}

印刷するだけです:

アミット
パタク
アロン

なぜこのような動作です。Enumeratorそれはスレッドセーフだと言えますか。

編集: Iterator を使用するとConcurrentModificationException、シングル スレッド アプリケーションがスローされます。

public static void main(String[] args) {

    Vector<String> v=new Vector<String>();
    v.add("Amit");
    v.add("Raj");
    v.add("Pathak");
    v.add("Sumit");
    v.add("Aron");
    v.add("Trek");

    Iterator<String> it=v.iterator();
    while(it.hasNext())
    {
        String value=(String) it.next();
        System.out.println(value);
        v.remove(value);
    }
}

チェックしてください。

4

7 に答える 7

12

ConcurrentModificationException は、マルチスレッドまたはスレッド セーフという意味での同時実行性とは関係がないことに注意してください。同時変更を許可するコレクションもあれば、許可しないコレクションもあります。通常、ドキュメントで答えを見つけることができます。ただし、並行とは、異なるスレッドによる並行という意味ではありません。これは、繰り返しながらコレクションを変更できることを意味します。

ConcurrentHashMap は、スレッドセーフであり、反復中に編集可能であることが明示的に定義されているため、特殊なケースです (これはすべてのスレッドセーフ コレクションに当てはまると思います)。

とにかく、単一のスレッドを使用してコレクションを反復および変更している限り、 ConcurrentHashMap は問題に対する間違った解決策です。API の使い方が間違っています。アイテムを削除するには、 Iterator.remove()を使用する必要があります。または、元のコレクションを反復して変更する前に、コレクションのコピーを作成することもできます。

編集:

ConcurrentModificationException をスローする Enumeration を知りません。ただし、同時変更の場合の動作は、期待したものとは異なる場合があります。例でわかるように、列挙はリスト内の 2 つおきの要素をスキップします。これは、削除に関係なく内部インデックスがインクリメントされるためです。これが起こることです:

  • en.nextElement() - Vector から最初の要素を返し、インデックスを 1 にインクリメントします
  • v.remove(value) - Vector から最初の要素を削除し、すべての要素を左にシフトします
  • en.nextElement() - Vector から 2 番目の要素を返します。現在は「Pathak」です。

Iterator のフェイルファスト動作は、このようなことからユーザーを保護します。これが、一般的に Enumberation よりも好ましい理由です。代わりに、次のことを行う必要があります。

Iterator<String> it=v.iterator();
while(it.hasNext())
{
    String value=(String) it.next();
    System.out.println(value);
    it.remove(); // not v.remove(value); !!
}

または:

for(String value : new Vector<String>(v)) // make a copy
{
    String value=(String) it.next();
    System.out.println(value);
    v.remove(value);
}

意図したとおりに API を使用する限り、実際にはコピーは必要ないため、最初のものは確かに望ましいものです。

于 2012-06-04T11:28:56.093 に答える
4

列挙は ConcurrentModificationException をスローしません。なぜですか?

呼び出されるコードには、この例外をスローするパスがないためです。編集:私は、Enumeration インターフェース全般についてではなく、Vector クラスによって提供される実装について言及しています。

なぜこのような動作です。Enumerator はスレッドセーフであると言えますか。

実行されるコードが適切に同期されるという意味で、スレッドセーフです。ただし、ループがもたらす結果は、あなたが例外とするものではないと思います。

出力の理由は、 Enumeration オブジェクトがカウンターを保持しているためです。このカウンターは、 の呼び出しごとに増分されますnextElement()。このカウンターは、 の呼び出しを認識していませんremove()

于 2012-06-04T11:16:50.677 に答える
3

ここでの同時変更は、スレッドとは何の関係もありません。

ここでの並行性とは、コレクションを反復処理しているときにコレクションを変更していることを意味します。(あなたの例では、これは同じスレッドで発生します。)

この場合、イテレータとコレクションの列挙をスローできますが、スローConcurrentModificationExceptionする必要はありません。フェイルファスト動作を示すもの。どうやら、の列挙はVectorフェイルファストではありません。

スレッドセーフには、明らかに複数のスレッドが含まれます。Vectorは、その操作(add、getなど)が同期されているという意味でのみスレッドセーフです。これは、たとえば、あるスレッドが要素を追加しているときに、同時に別のスレッドが要素を削除しようとしているときに、非決定的な動作を回避するためです。

これで、あるスレッドがコレクションを構造的に変更し、別のスレッドがコレクションを反復処理している場合、スレッドセーフと同時変更の両方の問題に対処する必要があります。ConcurrentModificationExceptionこの場合、そしておそらく一般的に、投げられることに依存しないことが最も安全です。適切なコレクションの実装(たとえば、スレッドセーフな実装)を選択し、同時変更を自分で回避/禁止するのが最善です。

一部のイテレータでは、イテレータ自体を介して要素を追加/設定/削除できます。これは、本当に同時変更が必要な場合に適した代替手段になる可能性があります。

于 2012-06-04T11:30:49.923 に答える
3

http://docs.oracle.com/javase/6/docs/api/java/util/Vector.htmlで読めるように、ConcurrentModificationException をスローするフェイルファスト動作は Iterator に対してのみ実装され ます。

于 2012-06-04T11:11:31.273 に答える
1

短い答え:bonus feature列挙が既に存在した後に発明された であるため、列挙子がそれをスローしないという事実は、特に何も示唆していません。

長い答え:
ウィキペディアから:

JDK 1.2 より前のコレクションの実装 [...] には、コレクション フレームワークが含まれていませんでした。Java オブジェクトをグループ化するための標準的な方法は、配列、Vector、および Hashtable クラスを使用するものでしたが、残念ながら拡張が容易ではなく、標準のメンバー インターフェイスを実装していませんでした。再利用可能なコレクション データ構造の必要性に対処するため [...] コレクション フレームワークは主に Joshua Bloch によって設計および開発され、JDK 1.2 で導入されました。

ConcurrentModificationExceptionBloch チームがこれを行ったとき、マルチスレッド プログラムでコレクションが適切に同期されなかった場合にアラーム ( ) を送信する新しいメカニズムを組み込むのは良い考えだと考えました。このメカニズムについては、次の 2 つの重要な点に注意してください。1) 並行処理のバグを検出できる保証はありません。例外は、運が良ければスローされます。2)単一のスレッドを使用してコレクションを誤用した場合にも例外がスローされます(例のように)。

したがって、 ConcurrentModificationException複数のスレッドによってアクセスされたときに をスローしないコレクションは、それがスレッドセーフであることも意味しません。

于 2012-06-04T11:40:29.477 に答える
0

Enumeration の取得方法によって異なります。次の例を参照してくださいConcurrentModificationException

import java.util.*;

public class ConcurrencyTest {  
    public static void main(String[] args) {  

        Vector<String> v=new Vector<String>();
        v.add("Amit");
        v.add("Raj");
        v.add("Pathak");
        v.add("Sumit");
        v.add("Aron");
        v.add("Trek");

        Enumeration<String> en=Collections.enumeration(v);//v.elements(); 

        while(en.hasMoreElements())
        {
            String value=(String) en.nextElement();
            System.out.println(value);
            v.remove(value);
        }              

        System.out.println("************************************");

        Iterator<String> iter = v.iterator();  
           while(iter.hasNext()){  
              System.out.println(iter.next());  
              iter.remove();  
              System.out.println(v.size());  
        }  
    }  
}  

列挙は単なるインターフェースであり、実際の動作は実装に依存します。Collections.enumeration() 呼び出しからの Enumeration は何らかの方法で反復子をラップするため、確かにフェイルファーストですが、Vector.elements() の呼び出しから取得された Enumeration はそうではありません。

フェイルファストではない列挙は、将来の不確定な時点で任意の非決定論的動作を導入する可能性があります。たとえば、メイン メソッドを次のように記述すると、最初の繰り返しの後に java.util.NoSuchElementException がスローされます。

public static void main(String[] args) {  

        Vector<String> v=new Vector<String>();
        v.add("Amit");
        v.add("Raj");
        v.add("Pathak");

        Enumeration<String> en = v.elements(); //Collections.enumeration(v);

        while(en.hasMoreElements())
        {   
            v.remove(0);
            String value=(String) en.nextElement();
            System.out.println(value);
        }       
    }  
于 2012-06-04T14:53:33.060 に答える
-1

remove the v.remove(value) and everything will work as expected

Edit: sorry, misread the question there

This has nothing to do with threadsafety though. You're not even multithreading so there is no reason Java would throw an exception for this.

If you want exceptions when you are changing the vector make it Unmodifiable

于 2012-06-04T11:00:43.000 に答える