Java并发性:final字段(在构造函数中初始化)是线程安全的吗?
有人能告诉我这个类是否是线程安全的吗Java并发性:final字段(在构造函数中初始化)是线程安全的吗?,java,concurrency,java-memory-model,Java,Concurrency,Java Memory Model,有人能告诉我这个类是否是线程安全的吗 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"); } pu
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);
}
}
还是不
编辑:我发现这与我的问题非常接近是的,只要这是整个类定义,而不是其中的一个片段
关键的事实是,
aMap
的内容在构建后无法修改。是的。无法修改参考aMap
本身,也无法将其添加到构造器之后的映射中(禁止反射)
如果公开aMap
,则不会公开,因为两个线程可以同时修改映射
您可以通过或使
aMap
不可修改来改进您的类。现在应该是线程安全的。但是,如果您添加了修改hashmap的其他方法,则不会。这个类没有并发问题,因为您只公开了一个get方法。如果您添加了一些修改映射的方法,则必须将此方法标记为已同步
具有不可变类,以使这类操作更简单并保证不可变:
private final ImmutableMap<String, String> aMap = ImmutableMap.of(
"1", "a",
"2", "b",
"3", "c");
private final ImmutableMap aMap=ImmutableMap.of(
“1”、“a”,
“2”、“b”,
“3”、“c”);
正如前面指出的,它是绝对线程安全的,final
由于其内存可见性效应,在这里非常重要
final
的存在保证了在构造函数完成后,其他线程将在映射中看到值,而无需任何外部同步。如果没有final
,则无法保证在所有情况下都能使用它,并且在将新构造的对象提供给其他线程时,需要使用安全的发布习惯用法,即(从):
- 从静态初始值设定项初始化对象引用李>
- 将对它的引用存储到易失性字段或原子引用中李>
- 将对它的引用存储到正确构造的对象的最终字段中;或
- 将对它的引用存储到由锁正确保护的字段中
我认为上面的代码片段不是线程安全的。代码安全的唯一一行是
aMap = new HashMap<String, String>();
这意味着一旦初始化了最终字段,就不能保证线程安全。因为只有引用赋值被保证是线程安全的,并且对象本身可以是可变的,正如您的示例所示。以下语句可能不是线程安全的
aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");
EDITMy bad稍后看到代码下面的注释
查看字段正确构造的值的能力很好,但是如果字段本身是引用,那么您还希望代码能够查看它所指向的对象(或数组)的最新值。如果您的字段是最终字段,这也是有保证的。因此,您可以拥有指向数组的最终指针,而不必担心其他线程看到数组引用的正确值,但数组内容的值不正确。同样,这里的“正确”是指“截至对象构造函数结束时的最新值”,而不是“可用的最新值”
这是很久以前提出的问题。不过,我决定回答这个问题。首先,它是完全线程安全的代码。原因如下
get
,它发布不可变的对象,这些对象对于并行线程来说是安全的,因为它们不能修改发布的对象final
,这使得它不可变并提供内存可见性回答这个问题,它实际上是安全的初始化。没有物体逃逸。其他线程看不到。正如第16.3节Java并发实践中所述,这必须是线程安全的 初始化安全保证了正确构造 对象,所有线程将看到 是由构造函数设置的,而不管对象是如何设置的 出版。此外,任何可以通过 正确构造的对象的最终字段(例如 最终数组或由最终数组引用的哈希映射的内容 字段)也保证对其他线程可见 对于具有最终字段的对象,初始化安全禁止 在初始荷载为 对该对象的引用。对最终字段的所有写入由 构造函数,以及通过这些 字段,在构造函数完成时变为“冻结”,以及任何线程 获取对该对象的引用的 这至少与冻结值一样是最新的。写道 不重新排序可通过最终字段访问的初始化变量 施工后冻结后的运营 这一节的一个例子是:
@ThreadSafe
public class SafeStates {
private final Map<String, String> states;
public SafeStates() {
states = new HashMap<String, String>(); states.put("alaska", "AK");
states.put("alabama", "AL");
...
states.put("wyoming", "WY");
}
public String getAbbreviation(String s) {
return states.get(s);
}
}
@ThreadSafe
公共类安全国家{
私人最终地图国家;
公共安全国家(){
states=newhashmap();states.put(“阿拉斯加”、“AK”);
州。put(“阿拉巴马州”、“阿尔卑斯州”);
...
州。出售(“怀俄明州”、“怀俄明州”);
}
公共字符串(字符串s){
返回状态。获取(s);
}
}
是的,但最后一个关键字的出现与此完全无关,因此看起来仍然有些混乱。如果你能解释一下你所期望发生的事情,我们也许能提供更多帮助。啊哈!看看你现在在问什么。很明显,我花了太多时间在有管理的独生子豆子上。是的,如果您阅读了java语言规范第17.5节的全部内容(您链接的文章对其进行了总结),那么它提到了对象引用
aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");
@ThreadSafe
public class SafeStates {
private final Map<String, String> states;
public SafeStates() {
states = new HashMap<String, String>(); states.put("alaska", "AK");
states.put("alabama", "AL");
...
states.put("wyoming", "WY");
}
public String getAbbreviation(String s) {
return states.get(s);
}
}