【发布时间】:2014-08-22 08:57:02
【问题描述】:
这是我的单身课程。
静态instance 字段不是易失性的,因此会出现重新排序/可见性问题。为了解决这个问题,实例 val 字段是最终的。由于实例是正确构造的,因此如果他们看到实例,它的客户应该总是看到 val 字段已初始化。
static class Singleton {
private static Singleton instance;
private final String val;
public Singleton() { this.val = "foo"; }
public static Singleton getInstance() {
if (instance == null)
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
return instance;
}
public String toString() { return "Singleton: " + val; }
}
但是还有另一个问题 - 我有两个不受保护的“实例”字段读取,可以(?)重新排序,以便客户端可能会得到空值而不是实际值:
public static Singleton getInstance() {
Singleton temp = instance;
if (instance != null) return temp;
else { /* init singleton and return instance*/ }
}
为了解决这个问题,我觉得我可以引入局部变量:
public static Singleton getInstance() {
Singleton temp = instance;
if (temp == null)
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
temp = instance;
}
}
return temp;
}
这似乎解决了问题,因为只有一个不受保护的值读取,所以不会发生真正的邪恶。但是......我刚刚修改了程序流程,而没有(几乎?)改变它的单线程语义。这是否意味着编译器可以撤消我的解决方法,因为这种转换是安全的,并且如果不与 volatile 建立适当的先发生关系,就无法使此代码正常工作?
【问题讨论】:
-
从 Java 1.5 开始,单例实现的问题已经用枚举解决了:stackoverflow.com/a/8027815/32090
-
这个问题实际上与单身人士无关。而是关于双重检查锁定本身。
-
这些问题非常难以解决。这就是为什么我们应该使用 Java 从
java.util.concurrent包中提供的抽象结构。
标签: java volatile java-memory-model double-checked-locking