维基百科关于单例的文章提到了一些在Java中实现该结构的线程安全方法。对于我的问题,让我们考虑具有冗长初始化过程并且可以同时被多个线程访问的singleton。
首先,这个未提及的方法是线程安全的吗?如果是,它是如何同步的?
public class Singleton {
private Singleton instance;
private Singleton() {
//lots of initialization code
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
第二,为什么下面的实现线程安全并且在初始化时懒惰?如果两个线程同时进入getInstance()
方法,究竟会发生什么?
public class Singleton {
private Singleton() {
//lots of initialization code
}
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
最后,在第二个示例中,如果一个线程首先获得实例,另一个线程获得实例并试图在第一个线程的构造函数完成之前对其执行操作,该怎么办?你会进入不安全的状态吗?
答案1:static synchronized
方法使用类对象作为锁-即在本例中是Singleton.class
。
答案2:java语言,其中包括:
- 在类第一次被访问/使用时加载
- 保证在允许访问类之前,所有静态初始化器都已完成
这两个事实意味着内部静态类SingletonHolder
直到getInstance()方法被调用才被加载。此时,在给予调用线程访问它的权限之前,该类的静态实例作为类加载的一部分被实例化。
这个模式是用于单例的模式。它打败了其他模式,因为MyClass.getInstance()
实际上是单例的行业标准——每个使用它的人都知道他们正在处理一个单例(对于代码,显而易见总是好的),所以这个模式有正确的API 和在底层的正确实现。
顺便说一句,在理解单例模式时,Bill Pugh的文章值得一读。