2011-06-23 8 views
12

誰でもこのクラスがスレッドセーフであるかどうかを教えてもらえますか?Javaの同時実行性:最終フィールド(コンストラクタで初期化される)はスレッドセーフですか?

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による:

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

これは、aマップに設定されたものがaMap = new HashMap<String, String>();であることを私に混乱させました。だから他のスレッドはこれらを見ることができます

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

またはそうではありませんか?

編集:私は、まさに私の質問に閉じ、このquestionを見つけ

+1

しかし、最終的なキーワードの存在はそれとはまったく関係がないため、まだ混乱があるようです。あなたが何が起こっているかを説明したなら、私たちはもっと助けることができるかもしれません。 – Affe

+2

ああ!あなたが今尋ねていることを見てください。明らかに、私はマネージドシングルトンビーンの土地で過ごす時間が多すぎます。はい。もし、あなたがリンクした記事が要約するJava言語仕様のセクション17.5のすべてを読んでいれば、最終フィールドで参照されるオブジェクトも構築終了時に最新のものであることが言及されています。 – Affe

答えて

14

すでに指摘したように、それは絶対にスレッドセーフだし、finalは、そのメモリの可視性の影響にここで重要なのです。

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

  • からオブジェクト参照を初期化します静的初期化子。
  • 揮発性フィールドまたはAtomicReferenceに参照を格納する。
  • 適切に構築されたオブジェクトの最終フィールドにそれへの参照を格納する。または
  • ロックによって適切に保護されているフィールドに参照を格納します。
1

はいそれは、提供されるこのクラス定義全体ではなくその抜粋です。

aMapの内容は、施工後に変更することはできません。

+1

実際、最終的なキーワードは非常に重要です。理由についての良い説明については、http://jeremymanson.blogspot.com/2008/04/immutability-in-java.htmlを参照してください。 –

6

はいです。参照aMap自体を変更する方法や、コンストラクタの後にマップに追加する(反射を禁止する)方法はありません。

aMapを公開すると、2つのスレッドがマップを同時に変更できるため、aMapが表示されません。

Collections.unmodifiableCollectionまたはCollections.unmodifiableMapaMapを変更できないようにすることで、クラスを改善できます。

0

今のところ、スレッドセーフである必要があります。しかし、ハッシュマップを変更する他のメソッドを追加すると、

1

このクラスは、getメソッドのみを公開するため、並行性の問題はありません。マップを変更するメソッドを追加する場合は、このメソッドを​​とマークする必要があります。

2

Guavaは、簡単にこの種のものを作るための不変クラスがあり、不変の保証:

private final ImmutableMap<String, String> aMap = ImmutableMap.of(
    "1", "a", 
    "2", "b", 
    "3", "c"); 
0

私は上記のコードスニペットはスレッドセーフではないと思います。コード安全であるだけの行は、これが最後のフィールドが初期化されるとの保証なしスレッドの安全性がないことを意味

aMap = new HashMap<String, String>(); 
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.htmlに与えられた例のように

class FinalFieldExample { 
    final int x; 
    int y; 
    static FinalFieldExample f; 
    public FinalFieldExample() { 
     x = 3; 
     y = 4; 
    } 

    static void writer() { 
     f = new FinalFieldExample(); 
    } 

    static void reader() { 
    if (f != null) { 
     int i = f.x; // x is guaranteed to be 3 
     int j = f.y; // y can have any value 
    } 
    } 
} 

です。参照の割り当てだけがスレッドセーフであることが保証され、オブジェクト自体はあなたの例に応じて変更可能であるためです。声明に続いて私の悪いフィールドに正しく構築された値を表示する機能がいいです後で

コード以下のコメントを見ましたが、フィールド場合は、スレッドセーフ

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

EDITではないかもしれませんそれ自体が参照である場合、コードが指すオブジェクト(または配列)の最新の値をコードに表示させたい場合もあります。フィールドが最終フィールドである場合は、これも保証されます。したがって、配列への最終的なポインタを持つことができ、配列参照の正しい値を見ている他のスレッドは気にする必要はありませんが、配列の内容の値は間違っています。ここでも「正しい」とは、「利用可能な最新の値」ではなく、「オブジェクトのコンストラクタの終わりに最新の状態」を意味します。

関連する問題