我一直在求职面试,我发现了这个问题:
以下Singleton工厂方法getInstance()?
有什么问题public class Singleton {
private static Singleton mySingleton;
protected Singleton() {}
public static Singleton getInstance() {
if (mySingleton == null) {
synchronized(Singleton.class) {
if(mySingleton == null) {
mySingleton = new Singleton();
}
}
}
return mySingleton
}
}
我知道构造函数是错误的,应该是私有的。然而,他们一直在询问getInstance()方法有什么问题,一切看起来都不错,我已经看过很多像这样的例子(在多线程环境中)我是什么丢失?
答案 0 :(得分:4)
原始代码的最大问题在于它是 Double Check 反模式的典型示例。它看起来很棒,但只是破碎了。更多内容如下: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
请注意,从java 1.5开始,有一种方法可以使用它,同步模式或更好的静态初始化是真正的方法。
-edit (我刚刚注意到这是为了求职面试。拔出"仔细检查"如果他们展示这个例子会很好。如果被问到详细信息,我会选择你没有完全复杂的java内存问题理解,但接受事实,以避免仔细检查。祝你好运)
答案 1 :(得分:1)
这似乎是线程安全的,但代码比它需要的更复杂。另外,使用锁具还有性能成本。获取锁需要一些时间,并且试图执行该代码的其他线程被阻止。
这两个问题可以通过使用静态初始化程序并完全从getInstance方法中删除同步来解决。 JVM将确保此语句只执行一次。结果是代码明显更简单,代码性能稍高一些。
public class Singleton {
private static Singleton mySingleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return mySingleton;
}
}
答案 2 :(得分:0)
我错过了什么?
JLS 17中描述的可能的记忆效应,因此写入和读取mySingleton之间没有发生过关系。这是违反直觉的,但如果您检查mySingleton不为null,则可以看到错误的对象。即在这个例子中mySingleton可以同时写入和读取,它被称为数据竞争。要修复此程序,您只需添加volatile即可。根据规范,它创建发生在写和读之间的关系之前。
答案 3 :(得分:-1)
难道你有点过于冗余吗?怎么样呢?
public static synchronized Singleton getInstance()
{
if (mySingleton == null)
mySingleton = new Singleton();
return mySingleton;
}
...或
public static synchronized Singleton getInstance()
{
return mySingleton==null?mySingleton=new Singleton():mySingleton;
}
赞扬Polywhirl先生