我已经阅读了很多,但还没有找到明确的答案。
我有一个如下所示的类(class):
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 在静态初始化器中初始化,之后不再修改(只读)。
我读到的是不可变对象(immutable对象)本质上是线程安全的。但我只在似乎是实例变量的上下文中看到了这一点。不可变的静态变量是线程安全的吗?
我发现的另一个构造是 ConcurrentHashMap。我可以制作 ConcurrentHashMap 类型的 sharedData,但它包含的 HashMaps 也必须是 ConcurrentHashMap 类型吗?基本上..
private static final ConcurrentHashMap<String, HashMap> sharedData;
或
private static final ConcurrentHashMap<String, ConcurrentHashMap> sharedData;
或者它会更安全(但简单地克隆()成本更高)?
this.myCopyOfData = sharedData.get(key).clone();
TIA。
(静态初始化器已被编辑以提供更多上下文。)
最佳答案
对 sharedData
的 reference 是 final 的,它是线程安全的,因为它永远无法更改。 Map 的内容是 NOT 线程安全的,因为它需要最好用 Guava ImmutableMap
实现或 java.util.Collections.unmodifiableMap()< 包装
或使用 java.util.concurrent
包中的 Map 实现之一。
只有BOTH你才能在 map 上获得全面的线程安全。任何包含的 Map 都必须是不可变的,或者也是并发实现之一。
.clone() 根本坏掉了,远离
默认情况下克隆是浅克隆,它只会返回对容器对象的引用而不是完整的副本。在普遍可用的信息中详细记录了原因。
关于java - Java中的最终静态变量是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2412124/