概述
单例模式–双重检验锁真的线程安全吗
单例模式是我们最熟悉不过的一种设计模式,用来保证内存中只有一个对象的实例。虽然容易,但里面的坑也有很多,比如双重检验锁模式(double checked locking pattern)真的是线程安全的吗?
这里涉及到2个知识点:
1、类初始化步骤
2、指令重排
熟悉类初始化机制的同学们都应该知道类初始化分三步
1、分配对象的内存空间
2、初始化对象,调用构造方法
3、将内存地址指向instance
指令重排就是为了优化性能在不改变结果的情况下CPU按照自己的顺序去执行指令
什么是双重检验锁模式
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
我们看到,在同步代码块的内部和外部都判断了instance == null,这时因为,可能会有多个线程同时进入到同步代码块外的if判断中,如果在同步代码块内部不进行判空的话,可能会初始化多个实例。
问题所在
这种写法看似完美无缺,但它却是有问题的,或者说它并不担保一定完美无缺。主要原因在于instance = new Singleton();并不是原子性的操作。
当instance指向分配地址时,instance不为空
但是,2、3部之间,可能会被重排序,造成创建对象顺序变为1-3-2.试想一个场景:
线程A第一次创建对象Singleton,对象创建顺序为1-3-2;
当给instance分配完内存后,这时来了一个线程B调用了getSingleton()方法
这时候进行instance == null的判断,发现instance并不为null。
但注意这时候instance并没有初始化对象,线程B则会将这个未初始化完成的对象返回。那B线程使用instance时就可能会出现问题,这就是双重检查锁问题所在。
使用volatile
对于上述的问题,我们可以通过把instance声明为volatile型来解决
public class Singleton{
private volatile static Singleton instance;
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
但是必须在JDK5版本以上使用。
静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种写法是目前比较推荐的一种写法,采用静态内部类的方式,即实现了懒加载又不会出现线程安全问题。而且减少了synchronized的开销。
Learn more
双重检查锁定与延迟初始化
PMD-DoubleCheckedLocking
Double-checked locking: Clever, but broken
最后
以上就是文艺黑米为你收集整理的单例模式-双重锁校验的全部内容,希望文章能够帮你解决单例模式-双重锁校验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复