2017-05-18 10 views
-2

私は実際にjavaの並行性を読んでいます。いくつかの問題があります。私は理解できません。 たとえば、javaスレッドセーフ:スレッドセーフですか?

package com.thread; 

import java.util.Collections; 
import java.util.HashSet; 
import java.util.Random; 
import java.util.Set; 

public class HiddenIterator { 
    private final Set<Integer> set = Collections.synchronizedSet(new HashSet<Integer>()); 
    public void add(Integer i) { 
     synchronized (set) { 
      set.add(i); 
     } 
    } 
    public void remove(Integer i) { 
     synchronized (set) { 
      set.remove(i); 
     } 
    } 
    public void addTenThings() { 
     Random random = new Random(); 
     for (int i = 0; i < 10; ++i) { 
      add(random.nextInt()); 
     } 
     //Hidden Iterator! 
     System.out.println("DEBUG: added ten elements to " + set); 
    } 
} 

は、プログラムのスレッドセーフですか? 編集しない場合はどうすればいいですか?

+0

[Java Concurrency Tutorial](https://docs.oracle.com/javase/tutorial/essential/concurrency/sync)があります。html)を参考にしてください。 –

+0

@ D.B。 JCIPほどうまくいきません... – shmosel

+0

@shmoselおそらく誰かが1つのリソースが混乱していると分かったり、理解しづらい場合は、別のリソースを読むことをお勧めします。ある人が他の人にとって意味をなさないかもしれないことには意味があります。 –

答えて

2

addremoveの呼び出しは同時に実行されないため、セットへのアクセスの一部はスレッドセーフです。

ただし、末尾のSystem.out行は、メッセージを作成するときにtoStringを呼び出し、toStringはセットの要素を反復処理する必要があります。 synchronizedSetを使用しましたが、それは個々の要素へのアクセスのみを保護します。では反復処理中は変更しません。 System.out行が実行されている間に他のスレッドが要素を追加および削除している場合、メッセージに表示される数値は予測できません。その行の周りに​​ブロックが必要です。メッセージの作成中にセットの内容を「フリーズ」する必要があります。

個の個別のaddコールだけが同期されるため、追加される個々のアイテムの間に他のスレッドがセットを見ることができます。これは、他のスレッドが10個のアイテムのうちのいくつかだけを持つリストを参照する可能性があることを意味します。あなたのプログラムがそのリストを使用しているものに応じて、それは問題になるかもしれません。

アトミックに10個の要素を追加する必要がある場合、他のスレッドがそれらのすべてを表示するか、またはどちらも表示しないようにするには、addTenThingsメソッドのループの周りに​​ブロックを配置します。

Collections.synchronizedSet​​ブロックの両方を使用する必要はありません。どちらか一方がOKです。相違点は次のとおりです。あなたはそれが必要だ場所に同期させることを忘れないことができるように

  • Collections.synchronizedSetは、セットへすべてアクセスを保護します。ただし、セット上の個々のメソッド呼び出しを保護することしかできません。特に、ループを実行している間は他のスレッドによってアイテムを追加したり削除したりすることができるため、セットを反復処理すると予測できない結果につながります。
  • ​​ブロックは、アトミック操作として機能するように、複数のメソッド呼び出しを保護することができますが、他のブロックに対してのみ保護します。​​を使用することを忘れないでください。
+0

'System.out.println'が' toString'を介してセットを反復しようとしていることを覚えておいてください。 –

+0

おっと、私はそれに気付かなかった。良いキャッチ;編集します。 – Wyzard

+0

この回答は重要なポイントです。場所にある公式のJavadocを含む多くのものは、同期メソッドを「スレッドセーフ」と呼びますが、必ずしも正確ではありません。メソッドの同期のみが存在する場合は、通常、スレッドセーフな方法で型を使用する方法があります。 –

1

一部はあまりにも安全ですが、一部は十分安全ではありません。 add()remove()は、synchronizedSetラッパーによって自動的に行われるため、内部で明示的に同期する必要はありません。あなたがsetを連結するとき、それは暗黙のうちにその要素の上にset.toString()、内部で繰り返し処理を呼び出すため説明したように

しかし、あなたは、println()声明の周りに同期させるために明示的な同期せずに安全ではありません(「隠されたイテレータを」)、必要なのですかdocumentationにあります。

関連する問題