psc*_*ang 26 java static multithreading final hashmap
我已经阅读了很多,但没有找到明确的答案.
我有一个看起来像这样的课程:
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()) {
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想知道从Foo实例访问sharedData是否是线程安全的(如构造函数和doSomethingUseful()中所示).Foo的许多实例将在多线程环境中创建.
我的意图是sharedData在静态初始化程序中初始化,之后不再修改(只读).
我读过的是不可变对象本质上是线程安全的.但我只是在实例变量的上下文中看到了这一点.不可变的静态变量是否安全?
我找到的另一个构造是ConcurrentHashMap.我可以创建ConcurrentHashMap类型的sharedData但是它包含的HashMaps也必须是ConcurrentHashMap类型的?基本上..
private static final ConcurrentHashMap<String, HashMap> sharedData;
Run Code Online (Sandbox Code Playgroud)
要么
private static final ConcurrentHashMap<String, ConcurrentHashMap> sharedData;
Run Code Online (Sandbox Code Playgroud)
或者它会更安全(简单克隆()更昂贵)?
this.myCopyOfData = sharedData.get(key).clone();
Run Code Online (Sandbox Code Playgroud)
TIA.
(已编辑静态初始化程序以提供更多上下文.)
小智 24
该参考到sharedData这是最后的是线程安全的,因为它永远不会改变.Map的内容不是线程安全的,因为它需要最好用Guava ImmutableMap实现包装,java.util.Collections.unmodifiableMap()或者使用java.util.concurrent包中的Map实现之一.
只有当你做BOTH将你对地图综合线程安全.任何包含的Maps都需要是不可变的或者是并发实现之一.
默认情况下克隆是一个浅层克隆,它只返回对容器对象的引用而不是完整的副本.有关原因的一般可用信息中有详细记录.
静态初始化块中静态最终字段的初始化是线程安全的.但是,请记住静态最终引用指向的对象可能不是线程安全的.如果您引用的对象是线程安全的(例如,它是不可变的),那么您就是明确的.
除非您按照问题中的建议使用ConcurrentHashMap,否则不能保证外部HashMap中包含的每个HashMap都是线程安全的.如果不使用线程安全的内部HashMap实现,当两个线程访问相同的内部HashMap时,可能会出现意外结果.请记住,只有ConcurrentHashMap上的某些操作是同步的.例如,迭代不是线程安全的.
什么是线程安全的?当然,HashMap的初始化是线程安全的,因为所有Foo都共享相同的Map实例,并且除非静态init中发生异常,否则Map保证在那里.
但是,修改Map的内容绝对不是线程安全的.静态final意味着无法为另一个Map切换Map sharedData.但地图的内容是一个不同的问题.如果给定的密钥同时使用多次,则可能会出现并发问题.
是的,这也是线程安全的。在允许任何线程访问它们之前,将初始化您的静态类的所有最终成员。
如果该static块在初始化期间失败,ExceptionInInitializerError则将在第一次尝试初始化的线程中引发an 。随后尝试引用该类将引发NoClassDefFoundError。
通常,a的内容HashMap不能保证跨线程可见。但是,类初始化代码使用一个synchronized块来防止多个线程初始化该类。此同步将刷新映射(及其HashMap包含的实例)的状态,以便对所有线程正确可见-假设在类初始化器之外未对映射或其所包含的映射进行任何更改。
有关类初始化和同步要求的信息,请参见Java语言规范,第12.4.2节。
| 归档时间: |
|
| 查看次数: |
30115 次 |
| 最近记录: |