单例模式:双重效验锁的懒汉实现方式
单例模式
单例模式顾名思义就是指实例化一个对象,通过把构造方法私有化来禁止创建实例。
饿汉模式实现
饿汉模式的特点是在类加载的时候就创建并初始化一个实例,实例在整个程序运行期间都是唯一的
java
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
这里的getInstance()
必须是静态方法,因为静态方法可以通过类名.方法名的方式调用,而非静态方法则要创建实例,这与我们单例模式的规则不相符。
懒汉模式实现
懒汉模式的特点是需要的时候再创建实例,实例在整个程序运行期间都是唯一的。
java
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
上面代码在单线程模式下是没问题的,但是在多线程模式下就会存在线程安全问题。
如果在首次创建实例,多个线程同时调用getInstance()
方法,就有可能创建多个实例。
改进1 (创建多个实例)
我们可以进行对 getInstance()
方法进行加锁:
java
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
这样虽然解决刚刚线程安全的问题,但每次调用getInstance()
方法都要加锁,增加不少的开销。
我们发现上面线程安全问题只存在于首次创建实例的情况,因此我们只需要对instance
为空的时候单独处理即可。
改进2 (性能比较低)
当instance
为空的时候加锁,再判断一次是否为空即可:
java
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
粗略的看上去好像没什么问题,实际上这里还有一个指令重排序的问题
通过查阅资料知道instance = new Singleton();
这个代码在执行的时候实际是执行分3步骤执行:
java
memory = allocate(); //1.分配对象的内存空间
ctorInstance(memory); //2. 初始化对象
instance=memory; //3. 设置instance指向刚分配的内存地址
JVM在执行的时候可能就会优化成 1->3->2
的顺序执行
可能导致在多线程环境下,还没执行2
就已经被其他线程返回了一个刚分配的地址
同样存在线程安全问题
,需要使用volatile
关键字来禁止指令重排序
。
改进3 (volatile禁止指令重排序)
java
public class Singleton {
//volatile 防止指令重排 和可见性
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
//先判断对象是否已经实例化过,没有实例化才进入加锁代码
if (null == instance) {
//类对象加锁
synchronized (Singleton.class) {
//避免 singleTon== null时,第一个线程实例化后,进入阻塞状态的线程被唤醒后仍会进行实例化。
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
以上就是线程安全的懒汉模式。