单例模式之双重检测锁

Posted ring2

tags:

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

  先来看看双重检测锁的实现以及一些简要的说明(本文主要说明双重检测锁带来的线程安全问题):

  

/**
 * 单例模式之双检锁
 * @author ring2
 * 懒汉式升级版
 */
public class Singleton3 

    private static volatile Singleton3 instance;
    
    private Singleton3() 
    
    public static Singleton3 getInstance() 
        
        //首先判断是否为空
        if(instance==null) 
            //可能多个线程同时进入到这一步进行阻塞等待
            synchronized(Singleton3.class) 
                //第一个线程拿到锁,判断不为空进入下一步
                if(instance==null) 
                    /**
                     * 由于编译器的优化、JVM的优化、操作系统处理器的优化,可能会导致指令重排(happen-before规则下的指令重排,执行结果不变,指令顺序优化排列)
                     * new Singleton3()这条语句大致会有这三个步骤:
                     * 1.在堆中开辟对象所需空间,分配内存地址
                     * 2.根据类加载的初始化顺序进行初始化
                     * 3.将内存地址返回给栈中的引用变量
                     * 
                     * 但是由于指令重排的出现,这三条指令执行顺序会被打乱,可能导致3的顺序和2调换
                     * ??
                     */
                    instance = new Singleton3();
                
            
        
        return instance;
    

  由于指令重排导致3,2的顺序调换以及处于多线程场景,会导致以下问题的出现首先第一个线程执行到了3号指令(instance变量被分配了地址,不为null了),但对象未初始化。此时!第一个或者第二个if语句进行判断时结果为true,自然而然在使用instance时会出错。

  解决的方法便是在instance变量上加上volatile关键字,加上volatile关键字后会禁止该变量的指令重排,从而达到线程安全。关于volatile关键字。。。又可以说上一篇。在其他篇章。

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

双重检查锁单例模式为什么要用volatile关键字?

单例模式之双重检测锁

Java单例模式详解

深入理解设计模式-单例模式(饿汉单例模式懒汉单例模式双锁单例模式)

单例模式的双重检测

关于单例模式与对象池的思考