为什么是双重校验锁实现单例模式呢?
public class Singleton {
// volatile关键字可以防止jvm指令重排优化
// 因为 instance = new Singleton() 这句话可以分为三步:
// 1. 为 instance 分配内存空间;
// 2. 初始化 instance
// 3. 将 instance 指向分配的内存空间。
// 但是由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。
// 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。
// 例如:
// 线程T1执行了1和3,此时T2调用 getInstance() 后发现 instance 不为空,因此返回 instance, 但是此时的 instance 还没有被初始化。
// 使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。
private static volatile Singleton instance = null;
// volatile关键字的第二个作用,保证变量在多线程运行时的可见性:
// 在 JDK1.2 之前,Java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的。
//
// 而在当前 的 Java 内存模型下,线程可以把变量保存本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。
// 这就 可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数 据的不一致。
// 要解决这个问题,就需要把变量声明为 volatile,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行 读取。
// 私有构造方法
private Singleton(){}
public static Singleton getInstance(){
// 提高代码执行效率,由于单例模式只要一次创建实例即可,
// 所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁
if(instance == null){
// 同步代码块,每次只允许一个线程执行,其余线程 在同步池中等待
synchronized (Singleton.class){
// 防止同步池中被唤醒的线程,进入同步代码块中能够二次创建实例
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
-
第一次 if(instance==null)
:这个是为了代码提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance
方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例即可。 -
第二次if(instance==null)
:这个校验是防止二次创建实例。
假如有一种情况,当instance还未被创建时,线程t1调用getInstance方法,由于第一次判断instance==null,此时线程t1准备继续执行,但是由于资源被线程t2抢占了,此时t2页调用getInstance方法,同样的,由于instance并没有实例化,t2同样可以通过第一个if,然后继续往下执行,同步代码块,第二个if也通过,然后t2线程创建了一个实例instance。此时t2线程完成任务,资源又回到t1线程,t1此时也进入同步代码块,如果没有这个第二个if,那么,t1就也会创建一个instance实例,那么,就会出现创建多个实例的情况,但是加上第二个if,就可以完全避免这个多线程导致多次创建实例的问题。
所以, 两次校验都必不可少。
private static volatile Singleton instance = null
: volatile g关键字也必不可少,volatile可以防止jvm指令重排优化。
因为 instance = new Singleton()
这句话可以分为三步:
- 为 singleton 分配内存空间;
- 初始化 singleton;
- 将 singleton 指向分配的内存空间。
但是由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 getInstance() 后发现 instance 不为空,因此返回 instance, 但是此时的 instance 还没有被初始化。使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69731.html