这是我的单身课程。
静态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; }
}
但是还有另一个问题 - 我有两个不受保护的“实例”字段读取,可以(?)重新排序,以便客户端可能得到 null 而不是实际值:
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 建立适当的先发生关系,就无法使此代码工作?