6
List<String> list = new ArrayList<String>();
list.add("a");
...
list.add("z");

synchronized(list) {
    Iterator<String> i = list.iterator();
    while(i.hasNext()) {
        ...
    }
}

List<String> list = new ArrayList<String>();
list.add("a");
...
list.add("z");

List<String> synchronizedList = Collections.synchronizedList(list);

synchronized(synchronizedList) {
    Iterator<String> i = synchronizedList.iterator();
    while(i.hasNext()) {
        ...
    }
}

synchronized具体的には、同期されたリストがリストへのスレッドセーフなアクセスを提供するときに、2 番目のインスタンスで が必要な理由については明確ではありません。

4

3 に答える 3

6

反復をロックしない場合、ループ中に別のスレッドが反復を変更すると、ConcurrentModificationExceptionが発生します。

すべてのメソッドを同期しても、それを少しでも防ぐことはできません。

これ(および他の多くのもの)がCollections.synchronized*完全に役に立たない理由です。
のクラスを使用する必要がありますjava.util.concurrent。(そして、あなたはあなたが安全であることをどのように保証するかについて慎重に考える必要があります)

一般的な経験則として:

すべてのメソッドの周りでロックを叩くだけでは、何かをスレッドセーフにするのに十分ではありません。

詳細については、私のブログを参照してください

于 2012-08-29T16:06:41.013 に答える
3

synchronizedList各呼び出しをアトミックにするだけです。あなたの場合、ループは複数の呼び出しを行うため、各呼び出し/反復の間に別のスレッドがリストを変更できます。並行コレクションの 1 つを使用する場合、この問題は発生しません。

このコレクションが ArrayList とどのように異なるかを確認します。

List<String> list = new CopyOnWriteArrayList<String>();
list.addAll(Arrays.asList("a,b,c,d,e,f,g,h,z".split(",")));

for(String s: list) {
    System.out.print(s+" ");
    // would trigger a ConcurrentModifcationException with ArrayList
    list.clear(); 
}

リストは繰り返しクリアされますが、イテレータが作成されたときの内容であるため、次のように出力されます。

a b c d e f g h z 
于 2012-08-29T16:19:53.393 に答える
2

2 番目のコードは、同期リストの実装方法のために同期する必要があります。これはjavadocで説明されています:

ユーザーは、返されたリストを反復処理するときに手動で同期することが不可欠です。

2 つのコード スニペットの主な違いは、add操作の効果です。

  • 同期されたリストを使用すると、可視性が保証されますsynchronizedList.get(..)。たとえば、他のスレッドが呼び出した場合、新しく追加されたアイテムが他のスレッドに表示されます。
  • ArrayList を使用すると、他のスレッドは新しく追加されたアイテムをすぐには認識しない可能性があります。実際にはそれらをまったく認識しない可能性があります。
于 2012-08-29T16:20:50.387 に答える