B中。 Goetz在他的JCIP第16.3章中写道:
初始化安全意味着清单16.8中的
SafeStates
可能是 甚至通过不安全的懒惰初始化或存储安全发布 在没有公共静态字段的情况下引用SafeStates
同步[...]
代码:
@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.fet(s);
}
}
不安全的延迟初始化
@NotThreadSafe
public class UnsafeLazyInitialization{
private static Resource resource;
public static Resource getInstance(){
if(resource == null)
resource = new Resource();
return resource;
}
}
我不明白为什么以这种方式发布对象是安全的。 resource
引用不是volatile
,因此即使happens-before
是不可变的,在写入它(resource = new Resource()
)和后续读取之间也没有Resource
。< / p>
这样,任何未初始化resource
的线程都可能会观察到resource
的陈旧值。
答案 0 :(得分:2)
The UnsafeLazyInitialization is unsafe because one thread may set value of the resource
before constructor of Resource
is fully completed, so another thread will pick a reference to a partly initialised object.
答案 1 :(得分:0)
如果我正确理解这个问题,那就不是为什么@NotThreadSafe
示例不安全或为什么@ThreadSafe
示例安全。
这就是为什么两者的结合是安全的,即为什么以下是安全的:
public class UnsafeLazyInitialization{
private static SafeStates safeStates;
public static SafeStates getInstance(){
if (safeStates == null)
safeStates = new SafeStates();
return safeStates;
}
}
答案是,如果正在初始化单个final
字段,则构造函数末尾会出现“冻结操作”。该操作(您可以将其设想为StoreStore
屏障)可确保如果您看到非null
实例,则该实例将完全初始化。请记住,这并不意味着您无法观察到null
实例(实例读取仍然是活泼的)。
有关更多信息,请查看Aleksey Shipilev撰写的这篇精彩博文:Safe Publication and Safe Initialization in Java。
答案 2 :(得分:-2)
不同之处在于,不安全代码中存在&#34;静态&#34; 。 静态意味着&#34;资源&#34; object是 class 对象,而不是实例对象。