30

私はかなり読んだことがありますが、決定的な答えは見つかりませんでした。

私は次のようなクラスを持っています:

    public class Foo() {

        private static final HashMap<String, HashMap> sharedData;

        private final HashMap myRefOfInnerHashMap;

        static {
           // time-consuming initialization of sharedData
           final HashMap<String, String> innerMap = new HashMap<String, String>;
           innerMap.put...
           innerMap.put...
           ...a

           sharedData.put(someKey, java.util.Collections.unmodifiableMap(innerMap));
        }

        public Foo(String key) {
            this.myRefOfInnerHashMap = sharedData.get(key);
        }

        public void doSomethingUseful() {
            // iterate over copy
            for (Map.Entry<String, String> entry : this.myRefOfInnerHashMap.entrySet()) {
                ...
            }
        }
     }

そして、FooのインスタンスからsharedDataにアクセスするのがスレッドセーフかどうか疑問に思っています(コンストラクターとdoSomethingUseful()に示されているように)。Fooの多くのインスタンスは、マルチスレッド環境で作成されます。

私の意図は、sharedDataが静的初期化子で初期化され、その後は変更されないことです(読み取り専用)。

私が読んだことは、不変オブジェクトは本質的にスレッドセーフであるということです。しかし、私はこれをインスタンス変数のコンテキストのように見えるものでのみ見ました。不変の静的変数はスレッドセーフですか?

私が見つけた他の構成はConcurrentHashMapでした。タイプConcurrentHashMapのsharedDataを作成できますが、それに含まれるHashMapもタイプConcurrentHashMapである必要がありますか?基本的に..

private static final ConcurrentHashMap<String, HashMap> sharedData;

また

private static final ConcurrentHashMap<String, ConcurrentHashMap> sharedData;

それとも、より安全でしょうか(ただし、単にclone()を実行する方がコストがかかります)。

this.myCopyOfData = sharedData.get(key).clone();

TIA。

(静的初期化子は、より多くのコンテキストを提供するように編集されています。)

4

8 に答える 8

28

finalへの参照sharedDataは変更できないため、スレッドセーフです。Map のコンテンツは、できれば Guava実装でラップするか、パッケージ内の Map 実装のいずれかを使用する必要があるため、スレッドセーフではありません。ImmutableMapjava.util.Collections.unmodifiableMap()java.util.concurrent

両方を実行した場合にのみ、マップ上で包括的なスレッド セーフが得られます。含まれているすべてのマップは、不変であるか、並行実装の 1 つでもある必要があります。

.clone() は根本的に壊れています。

デフォルトのクローン作成は浅いクローンであり、完全なコピーではなく、コンテナ オブジェクトへの参照を返すだけです。その理由については、一般に入手可能な情報で十分に文書化されています。

于 2010-03-09T20:05:00.093 に答える
8

静的初期化ブロック内の静的最終フィールドの初期化は、スレッド セーフです。ただし、静的最終参照が指すオブジェクトはスレッドセーフではない可能性があることに注意してください。参照先のオブジェクトがスレッド セーフである (たとえば、不変である) 場合、問題はありません。

質問で提案されているように ConcurrentHashMap を使用しない限り、外側の HashMap に含まれる個々の HashMap はスレッドセーフであるとは限りません。スレッドセーフな内部 HashMap 実装を使用しない場合、2 つのスレッドが同じ内部 HashMap にアクセスすると、意図しない結果が生じる可能性があります。ConcurrentHashMap の一部の操作のみが同期されることに注意してください。たとえば、反復はスレッドセーフではありません。

于 2010-03-09T20:04:22.543 に答える
6

はい、これもスレッドセーフです。静的クラスのすべての最終メンバーは、スレッドがそれらにアクセスできるようになる前に初期化されます。

static初期化中にブロックが失敗した場合、ExceptionInInitializerError最初に初期化を試みるスレッドでが発生します。その後、クラスを参照しようとすると、が発生しNoClassDefFoundErrorます。

一般に、aの内容は、HashMapスレッド間の可視性を保証するものではありません。ただし、クラス初期化コードはsynchronizedブロックを使用して、複数のスレッドがクラスを初期化するのを防ぎます。この同期により、マップ(およびマップに含まれるインスタンス)の状態がフラッシュさHashMapれ、クラス初期化子の外部でマップまたはマップに含まれるマップに変更が加えられていないことを前提として、すべてのスレッドに正しく表示されます。

クラスの初期化と同期の要件については、 Java言語仕様の§12.4.2を参照してください。

于 2010-03-09T20:01:59.570 に答える
6

スレッドセーフとは?確かに、HashMap の初期化は、すべての Foo が同じ Map インスタンスを共有し、静的 init で例外が発生しない限り Map が存在することが保証されるという点で、スレッドセーフです。

しかし、Map の内容を変更することは、確実にスレッドセーフではありません。static final は、Map sharedData を別の Map に切り替えることができないことを意味します。しかし、マップの内容は別の問題です。特定のキーが同時に複数回使用されると、同時実行性の問題が発生する可能性があります。

于 2010-03-09T20:08:31.350 に答える
5

いいえ。不変である場合を除きます。

彼らがする唯一のことは

  • クラスレベルでアクセス可能
  • 参照を変更することは避けてください。

それでも、属性が変更可能である場合、それはスレッドセーフではありません。

参照:最終的なインスタンス変数を同期しますか?

クラスレベルであることを除けば、まったく同じです。

于 2010-03-09T20:16:11.517 に答える
3

final static変数について本質的にスレッドセーフなものはありません。メンバー変数を宣言final staticすると、この変数が1回だけ割り当てられることが保証されます。

スレッドセーフの問題は、変数をどのように宣言するかとは関係がなく、代わりに変数をどのように操作するかに依存します。したがって、プログラムの詳細がなければ、質問に答えることは実際には不可能です。

  • 複数のスレッドがsharedData変数の状態を変更しますか?
  • もしそうなら、あなたはのすべての書き込み(および読み取り)で同期しますsharedDataか?

ConcurrentHashMapを使用すると、の個々のメソッドMapがスレッドセーフであることが保証されるだけで、次のような操作はスレッドセーフになりません。

if (!map.containsKey("foo")) {
    map.put("foo", bar);
}
于 2010-03-09T20:04:06.943 に答える
1

sharedDataの静的初期化がスレッドセーフであり、一度だけ実行されるかどうかを実際に尋ねていませんか?

はい、そうです。

もちろん、ここにいる多くの人々は、 の内容をsharedData変更できることを正しく指摘しています。

于 2010-03-09T20:32:32.553 に答える
0

この場合、sharedDataオブジェクトのみが不変です。つまり、常に同じオブジェクトを操作するということです。ただし、その中のデータは、いつでも、どのスレッドからでも変更(削除、追加など)できます。

于 2010-03-09T20:04:07.300 に答える