以下是对单例模式的双重检查实现。线程A正在执行第test=new Test();
行但是同时,线程B首先检查test
的值。什么是test
和线程B的价值?
class Test {
private Test test;
private Test() {
}
public static Test get() {
if (test == null) { // Thread B. When object is being created,
// what's value of test. Is it always null before
// Thread B new object?
synchronized (test.getClass()) {
if (test == null) {
test = new Test(); // Thread A. This thread is creating object.
}
}
}
return test;
}
}
加
如果它不是单身模式的正确版本,volatile
关键字可以解决问题吗?也就是private volatile Test test;
答案 0 :(得分:1)
未知。这就是为什么这种实现被认为是错误的。
test = new Test()
有可能首先创建对象,然后将对它的引用分配给test
,然后才初始化它。
因此线程B
将引用object,但它可能未初始化。
答案 1 :(得分:0)
这个似乎也是 Bill Pugh 建议的一个好方法,以消除双重检查锁定的缺点(如问题所述)我们不知道我需要进行急切的初始化以消除这种情况:
public class BillPughSingleton {
private BillPughSingleton(){}
private static class SingletonHelper{
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
注意包含singleton类实例的private inner static class
。加载单例类时,SingletonHelper class
不会加载到内存中,只有当有人调用getInstance
方法时,才会加载此类并创建Singleton类实例。
这是Singleton类最广泛使用的方法之一,因为它不需要同步。
答案 2 :(得分:0)
如果它不是单例模式的正确版本,可以使用volatile关键字 解决问题?
确实,这个:
private Test test;
应该声明:
private volatile Test test;
否则线程B
的内存可能保持test
的陈旧版本可能为null
A
使用私有构造函数对其进行评估。
通过将test
指定为volatile
,您可以确保其他线程可以看到由一个线程执行的任何分配。
现在这种实现单例的方式不是很直接(双条件语句加显式同步)。
作为两个很好的选择,你可以使用enum singleton idiom:
public enum Test implements TestInterface{
SINGLETON;
...
}
请注意,建议接口编写一个可自然测试的实现,并能够切换到其他实现。
或者Bill Pugh单身成语(这里有热切的味道):
public class Test{
// executed as soon as the T class is loaded by the classLoader
private static Test instance = new Test();
private Test() {
}
public static TestgetInstance() {
return instance;
}
}