8

このメソッドの変数 'c​​ommonSet' が代わりにクラス レベル フィールドである場合、次のコードは同じ問題を引き起こしますか。クラス レベルのフィールドである場合、HashSet はスレッド セーフではないため、set 操作への追加を同期ブロック内にラップする必要があります。複数のスレッドがセットに追加されているか、現在のスレッドでさえセットを変更し続ける可能性があるため、次のコードで同じことを行う必要があります。

public void threadCreatorFunction(final String[] args) {
    final Set<String> commonSet = new HashSet<String>();

    final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                commonSet.add(newValue());
            }
        }
    };

    new Thread(runnable, "T_A").start();
    new Thread(runnable, "T_B").start();
}

「commonSet」への参照は、final を使用して「ロック」されています。ただし、その上で動作する複数のスレッドがセット内の値を破損する可能性があります (重複が含まれている可能性がありますか?)。次に、'commonSet' ia はメソッド レベルの変数であるため、混乱が生じます。同じ参照が、呼び出し元のメソッド (threadCreatorFunction) のスタック メモリと実行メソッドのスタック メモリになります。これは正しいですか?

これに関連して、かなりの数の質問があります。

しかし、そのようなミュータブルの共有/受け渡しのスレッドセーフな部分に重点を置いているのを見ることはできません。

4

6 に答える 6

9

いいえ、これは絶対にスレッドセーフではありません。最終変数に含まれているという理由だけで、両方のスレッドが同じ参照を参照することを意味します。これは問題ありませんが、オブジェクトをスレッドセーフにすることはできません。

アクセスを同期するか、を使用する必要がありますConcurrentSkipListSet

于 2012-08-06T07:44:53.230 に答える
5

興味深い例。

参照commonSetはスレッドセーフで不変です。Runnableこれは、最初のスレッドと匿名クラスのフィールドのスタックにもあります。(これはデバッガーで確認できます)

参照するセットcommonSetは変更可能であり、スレッドセーフではありません。スレッドセーフにするには、同期またはロックを使用する必要があります。(または、代わりにスレッドセーフコレクションを使用してください)

于 2012-08-06T07:44:55.163 に答える
1

最初の文で単語が抜けていると思います:

このメソッドの変数 'c​​ommonSet' が???代わりにクラス レベル フィールドである場合、次のコードは同じ問題を引き起こしますか。

でも、あなたは少し混乱していると思います。並行性の問題は、変更可能なデータ構造への参照が宣言されているかどうかとは関係ありませんfinal。の匿名内部クラス宣言内でそれを閉じてfinalいるため、参照を as として宣言する必要があります。実際に複数のスレッドでデータ構造の読み取り/書き込みを行う場合は、ロック (同期) を使用するか、 java.util.concurrent.ConcurrentHashMapなどの同時データ構造を使用する必要があります。Runnable

于 2012-08-06T07:50:34.000 に答える
1

commonSet は 2 つのスレッド間で共有されます。それを final として宣言したため、参照を不変 (再割り当てはできません) にしましたが、Set 内の実際のデータは依然として変更可能です。あるスレッドがデータを入力し、別のスレッドがデータを読み取るとします。最初のスレッドがデータを入れるたびに、そのデータが書き込まれるまで他のスレッドが読み取れないように、そのセットをロックする必要があります。それは HashSet で起こりますか? あまり。

于 2012-08-06T08:35:12.170 に答える
0

commonsetがシングルスレッドで使用されているか複数で使用されているかにかかわらず、最終オブジェクトに対して不変であるのは参照のみです(つまり、一度割り当てられると、別のobj参照を再度割り当てることはできません)が、その参照を使用してこのオブジェクトによって参照されるコンテンツを変更できます。

それが最終的でない場合、1つのスレッドがそれを再度初期化し、参照を変更した可能性があります commonSet = new HashSet<String>(); commonSet.add(newValue()); 。その場合、これらの2つのスレッドは、おそらくあなたが望むものではない2つの異なる共通セットを使用する可能性があります。

于 2012-08-06T08:14:19.830 に答える
0

他の人がすでにコメントしているように、ファイナルや同期などのいくつかの概念を誤解しています。

コードで達成したいことを説明していただければ、より簡単にお手伝いできると思います。このコード スニペットは、実際のコードよりも例にすぎないという印象を受けました。

いくつかの質問: 関数内でセットが定義されているのはなぜですか? スレッド間で共有する必要がありますか? 私を困惑させるのは、ランナブルの同じインスタンスで2つのスレッドをクレートすることです

    new Thread(runnable, "T_A").start();
    new Thread(runnable, "T_B").start();
于 2012-08-06T08:06:39.930 に答える