单例创建方法与双重检查锁定
Singleton类定义如下:
public class Singleton {
public static Singleton instance;
private Singleton() { ; } // 注意此处定义为私有构造方法
/**
* 获取单例
*/
public static Singleton getInstance() { ... }
}
单例创建的一般方法
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
显然一般方法对于单线程模式下的单例创建是有效且无误的。然而对于多线程情形,由于线程间竞争关系的存在,当线程1进入if语句块而并未成功创建instance实例时,此时若线程2进入if语句块,那么也将会创建新的实例,这并不是我们想要的单例模式。
单例创建的同步方法
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
利用synchronized方法,可以防止某线程在创建单例时,其他线程进入getInstance类方法去创建单例。然而这种方法存在性能上的缺陷,因为每次调用getInstance方法,都将付出线程同步的代价。
单例创建的双重锁定方法
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
// 在Singleton构造函数执行之前,instance变量可能变为非null的
instance = new Singleton();
}
}
}
return instance;
}
双重锁定方法可以解决性能缺陷问题,理论上只有一个线程会进入synchronized块,其他线程不会进入。然而Java内存模型并不保证每次都能达到理论效果,也许会存在无序写入的问题。
instance = new Singleton();
的伪码解释如下:
mem = allocate(); // allocate memory for Singleton object
instance = mem; // note that now instance is non-null, but has not been initialized
createSingleton(instance); // invoke constructor for Singleton passing instance
单例创建的饿汉式方法
static {
instance = new Singleton();
}
public static Singleton getInstance() {
return instance;
}
这种方法在类被加载时就创建实例对象,性能友好而内存不友好。