20

このクラスがスレッドセーフかどうか教えてもらえますか?

class Foo {

    private final Map<String,String> aMap;

    public Foo() {
        aMap = new HashMap<String, String>();
        aMap.put("1", "a");
        aMap.put("2", "b");
        aMap.put("3", "c");
    }

    public String get(String key) {
        return aMap.get(key);
    }

}

編集:質問を明確にしないのは私のせいです。JMM FAQによると:

初期化の安全性を新たに保証する必要があります。オブジェクトが適切に構築されている場合 (つまり、そのオブジェクトへの参照が構築中にエスケープされない場合)、そのオブジェクトへの参照を参照するすべてのスレッドは、コンストラクターで設定された最終フィールドの値も参照します。同期。

これにより、aMap へのセットがaMap = new HashMap<String, String>();. したがって、他のスレッドはこれらを見ることができます

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");

か否か ?

編集:私はこの質問が私の質問に正確に近いことを見つけました

4

9 に答える 9

22

すでに指摘したように、これは完全にスレッドセーフでありfinal、メモリの可視性効果のためにここで重要です。

finalコンストラクターが外部同期なしで終了した後、他のスレッドがマップ内の値を参照することを保証する存在。それがなければfinal、すべての場合に保証されるわけではなく、新しく構築されたオブジェクトを他のスレッドで使用できるようにするときは、安全なパブリケーション イディオムを使用する必要があります ( Java Concurrency in Practiceから)。

  • 静的初期化子からのオブジェクト参照の初期化。
  • それへの参照を volatile フィールドまたは AtomicReference に格納します。
  • それへの参照を、適切に構築されたオブジェクトの final フィールドに格納します。また
  • それへの参照を、ロックによって適切に保護されたフィールドに格納します。
于 2011-06-23T16:13:53.710 に答える
8

はい、そうです。参照aMap自体を変更したり、コンストラクターの後にマップに追加したりする方法はありません (リフレクションを除く)。

aMap2 つのスレッドが同時にマップを変更する可能性があるため、公開しても問題はありません。

Collections.unmodifiableCollectionまたはCollections.unmodifiableMapaMapを介して変更不可にすることで、クラスを改善できます。

于 2011-06-23T16:03:48.673 に答える
3

Guavaには、この種のことを簡単にし、不変を保証するための不変クラスがあります。

private final ImmutableMap<String, String> aMap = ImmutableMap.of(
    "1", "a",
    "2", "b",
    "3", "c");
于 2011-06-23T16:09:02.220 に答える
2

はい、これはクラス定義全体であり、そのスニペットではありません

aMap重要な事実は、 の内容を構築後に変更する方法がないということです。

于 2011-06-23T16:03:14.700 に答える
1

このクラスには同時実行の問題はありません。これは、get メソッドのみを公開するためです。マップを変更するメソッドを追加する場合は、このメソッドを としてマークする必要がありますsynchronized

于 2011-06-23T16:06:46.107 に答える
0

現在のように、スレッドセーフである必要があります。ただし、ハッシュマップを変更する他のメソッドを追加する場合は、いいえ。

于 2011-06-23T16:05:09.907 に答える