本文主要是介绍单例模式:双重效验锁的懒汉实现方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
单例模式:双重效验锁的懒汉实现方式
- 单例模式
- 饿汉模式实现
- 懒汉模式实现
- 改进1 (创建多个实例)
- 改进2 (性能比较低)
- 改进3 (volatile禁止指令重排序)
单例模式
单例模式顾名思义就是指实例化一个对象,通过把构造方法私有化来禁止创建实例。
饿汉模式实现
饿汉模式的特点是在类加载的时候就创建并初始化一个实例,实例在整个程序运行期间都是唯一的
public class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
这里的getInstance()
必须是静态方法,因为静态方法可以通过类名.方法名的方式调用,而非静态方法则要创建实例,这与我们单例模式的规则不相符。
懒汉模式实现
懒汉模式的特点是需要的时候再创建实例,实例在整个程序运行期间都是唯一的。
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()
方法进行加锁:
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
为空的时候加锁,再判断一次是否为空即可:
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步骤执行:
memory = allocate(); //1.分配对象的内存空间
ctorInstance(memory); //2. 初始化对象
instance=memory; //3. 设置instance指向刚分配的内存地址
JVM在执行的时候可能就会优化成 1->3->2
的顺序执行
可能导致在多线程环境下,还没执行2
就已经被其他线程返回了一个刚分配的地址
同样存在线程安全问题
,需要使用volatile
关键字来禁止指令重排序
。
改进3 (volatile禁止指令重排序)
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;}
}
以上就是线程安全的懒汉模式。
这篇关于单例模式:双重效验锁的懒汉实现方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!