Java 实现单例模式有方法有双重检测锁,代码如下:
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
服务器托管网 }
}
return singleton;
}
}
我理解的 synchronized 关键字实现了可见性、原子性和有序性,临界区中的代码可以重排序,但是不能重排序到临界区外面,synchronized 实现的可见性是临界区中代码执行结束之后,里面的共享变量会刷新到主内存中,那么如果 new Singleton() 方法被拆成了三个操作,并且经过重排序之后的顺序是这样的话:
- 分配内存
- 将实例引用赋值给 singleton 变量
- 实例初始化
不管这三个操作怎么重排序,另外一个线程看到的结果都是这三个操作执行完成后的结果(因为 synchronized 的原子性),那不就相当于另外一个线程访问到的 singleton 如果不为 null 的话就肯定实例化了吗?为什么还要多此一举加个 volatile 关键字禁止重排序呢?
在双重检测锁的单例模式实现中,将 singleton
变量标记为 volatile
是为了解决指令重排序问题。
指令重排序是指处理器在执行指令时可能会对指令进行重新排序,以提高执行效率。然而,由于多线程环境下的指令重排序可能导致单例对象的不正确初始化,因此需要使用 volatile
关键字来禁止重排序。
具体而言,在没有使用 volatile
的情况下,可能会发生以下情况:
- 线程A进入
getSingleton
方法中,发现singleton
为null,因此进入同步块中。 - 线程A在同步块中执行了分配内存、将实例引用赋值给
singleton
变量和实例初始化等操作,但是这些操作可能会被重排序。 - 在还没有完成实例初始化的情况下,线程B进入
getSingleton
方法,发现singleton
不为null(由于线程A的部分操作服务器托管网可能已经执行完成),于是直接返回singleton
。 - 线程B尝试使用
singleton
,但由于实例初始化的某些操作尚未完成,可能会导致错误。
通过将 singleton
声明为 volatile
,可以确保在每个线程中,所有的写操作(分配内存、初始化等)都发生在对 singleton
的读操作之前。这样就可以避免上述的问题。
综上所述,使用 volatile
关键字可以确保双重检测锁的单例模式在多线程环境下正确地实现延迟加载和单例对象的正确初始化。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: 浅谈设计模式-工厂模式的设计思想以及细节问题(上篇)
1什么是工厂模式? 工厂模式,顾名思义,就是把将对象的实例化过程封装在工厂类中的方式。工厂负责生产相应的对象实例。 一般分为两种工厂模式:简单工厂;抽象工厂 优点: 用户不需要解决具体的细节问题,利用工厂类进行生产产品细节; 可以将对象的创建与使用代码分离,提…