[英]Is there a threadsafe or visibility issue using “private static” field instead of “private final static” field
关于具有线程安全性并在类加载时由单个线程执行的静态初始化程序,如果“私有静态字段”使我理解相同的线程安全性和可见性保证,为什么还要在单例中使用“私有最终静态字段”-前提是静态初始化后不会触摸静态字段。
简而言之,我确实要说以下两个示例在多线程环境中表现相同:任何时候任何客户端都将返回相同的实例-甚至没有最微妙的差异-您同意吗? 请以任何一种方式让我知道。
静态字段示例:
public class Singleton {
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
private Singleton() {
}
}
最终静态字段示例:
public class Singleton_final {
private final static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
private Singleton() {
}
}
亲切的问候,赫尔曼
对于您给出的代码,没有什么区别,因为您没有实际设置Singleton
类的新实例的方法,因为您没有在类外可以看到的构造函数或设置器。
但是,这并不是说没有final
结果就可以更改Singleton
实例。 如果您没有final
限定符,则从理论上讲,Reflection API可用于更改实例。 我说的是理论上的原因,因为我自己从未尝试过,而且我想不出什么办法。 我向任何具有反射API经验的人开放
与许多多线程问题一样,它有点微妙。 请允许我深入探讨问题,然后提供一个答案。
如果客户看到一个非null
引用,他们只会看到您创建的一个Singleton
。 毫无疑问。 这是两个问题:
null
吗? Singleton
吗? 后一个问题与您的情况不太相关,因为Singleton
没有任何状态。 但是,如果它确实具有状态,并且该状态存储在非final
字段中,则将是一个问题。 例如,给定:
public class Singleton {
private /* non-final */ String name;
private Singleton(String name) {
this.name = name;
}
}
...部分构造的Singleton
是名称为null
的构造器(尽管在构造时已设置为非-null
值)。
您可能想要的是(a)没有人看到null
和(b)没有人看到部分构造的对象。 要做到这一点,您需要该类没有数据争用。 要做到这一点 ,你需要的之前发生关系。
所以问题的实质是: 在初始化instance
的线程和以后读取它的任何线程之间是否存在before-before关系?
捷豹路虎对此有些怀疑。 在JLS 12.4.2上有一个关于类初始化的详细描述,其中包括对类的锁定,因此引入了先发生后发生的情况。 但是JLS中没有任何东西可以指定当类已经初始化时会发生什么! 在那些情况下,JLS不需要任何锁定,因此不会建立任何事前发生的关系。 严格阅读JLS可能会建议其他线程中的客户端可以看到null
引用或部分构造的对象。
JLS暗示这不应在12.4.1中发生:
目的是类或接口类型具有一组初始化器,这些初始化器将其置于一致状态,并且该状态是其他类观察到的第一个状态。
好吧,这就是“意图”,这很好,但是没有任何要求(除了意图声明)。
甲final
字段(静态或不)得到特殊的线程安全语义( JLS 17.5 ,或看到这个问题上SO),其基本上提供上述之前发生边缘,并且因此消除了数据争用,并确保一个非null
到完全参考构造的对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.