いろいろ調べてみましたが、明確な答えは見つかりませんでした。
次のようなクラスがあります:
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();
ティア。
(静的初期化子は、より多くのコンテキストを提供するために編集されました。)
ベストアンサー1
の参照最終的なものはsharedData
変更できないためスレッドセーフです。マップの内容はないスレッドセーフである必要があるのは、できれば GuavaImmutableMap
実装でラップするか、パッケージjava.util.Collections.unmodifiableMap()
内の Map 実装の 1 つを使用する必要があるためですjava.util.concurrent
。
もしそうなら両方マップ上で包括的なスレッド セーフティが実現されます。含まれるマップはすべて不変であるか、並行実装の 1 つである必要があります。
.clone() は根本的に壊れているので、使用しないでください
デフォルトではクローンは浅いクローンであり、完全なコピーではなく、コンテナ オブジェクトへの参照のみを返します。その理由については、一般に公開されている情報に詳しく記載されています。