双重校验锁实现单例模式

Posted Peterxiazhen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双重校验锁实现单例模式相关的知识,希望对你有一定的参考价值。

package day1;
/** 特点:懒汉模式,只有使用时,才进行创建
* 首次调用getInstance()才使用synchronized加锁,后续使用时无需加锁
* 该代码存在效率问题,因为即使已经产生了单例之后,之后调用了getInstance()方法之后仍然还会加锁解锁,
* 加锁和解锁会影响系统的性能
* ====================>
* 于是有了双重校验锁机制
*
*/
public final class Singleton {
private static Singleton instance = null;
private Singleton() {} // 私有的构造方法,只能类内调用
public static Singleton getInstance() {
synchronized (Singleton.class) { // 避免多线程并发访问
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
}

final class Singleton_1 {
private Singleton_1() {
}

private static Singleton_1 instance = null;

public static Singleton_1 getInstance() {
if (instance == null) {
// 首次访问会同步,之后的不会进入synchronized方法
synchronized (Singleton_1.class) {
if (instance == null) {
instance = new Singleton_1();
}
}
}
return instance;
}
}
/**
* 但是该方法仍然存在问题
* * if (instance == null) 在synchronized外部,instance静态变量不是有序的,synchronized无法禁止指令重排
* * instance = new Singleton_1();
* * 字节码包含4部分:
* * 1. new 新建一个实例
* * 2. 复制这个实例的引用
* * 3. 通过该引用调用构造方法
* * 4. 引用用来进行赋值操作putstatic
* *
* * JIT即时编译器进行指令重排,将4排到3的前面,那么t1还未完全将构造方法执行完毕,如果在构造方法中要执行很多初始化操作
* * 那么t2拿到的instance类变量是非空的,但是一个未初始化完毕的单例,那么就会出现问题
* * 解决办法,就是对instance使用volatile修饰
*/

// 完整版
final class Singleton_2 {
private Singleton_2() {}
private volatile static Singleton_2 instance = null;
public static Singleton_2 getInstance() {
// 读屏障,写屏障之后的不能指令重排
if (instance == null) {
// 首次访问会同步,之后的不会进入synchronized方法
synchronized (Singleton_2.class) {
if (instance == null) {
instance = new Singleton_2();
// 写屏障,写屏障之前的不能指令重排
}
}
}
return instance;
}
}





以上是关于双重校验锁实现单例模式的主要内容,如果未能解决你的问题,请参考以下文章

单例模式之双重校验锁的优缺点

双重检查锁实现单例模式的线程安全问题

单例模式-双重校验锁

单例模式的双重校验锁模式 的知识点 解读

单例模式的双重校验锁模式 的知识点 解读

关于双重检验锁方法和内部类方法实现单例模式的实验