单例模式双重校验的再理解

        提起单例模式,作为攻城狮的你我都不会感觉到陌生,而为了确保在程序中的线程安全,我们常常会倾向于双重校验和静态类两种方式。而且众所周知,在双重校验的方式中,我们发现了关键字volatile的身影,而且一直以来小编只是知道 该关键字可以保证操作之间的可见性。但是只知其一啊,今天突然明白这其中的道理:

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}   
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
} 
      如上述代码片所示,singleton变量使用了volatile关键字修饰,也就意味着这个变量对接下来的操作具有可见性(原因稍后会有解释)。

♗  如果上述代码中singleton变量去掉volatile关键字……

public class Singleton {  
    private static Singleton singleton;  
    private Singleton (){}   
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
} 

     如上述代码所示,如果是单线程操作,由于代码的顺序间接的决定了执行顺序,而且在单线程中,即使jvm执行了顺序重排,仍然不会出现问题;

    在讨论多线程的场景之前,我们先来科普一下 对象初始化的过程:在对象初始化也就是如第八行代码(singleton = new Singleton();  )所示,要知道,这行代码一共有三个过程:

    分配对象的内存空间-->初始化对象 --> 将singleton指向刚分配好的内存地址

-----------------------------------------我是分割线-------------------------------------------

    明白初始化的过程之后,我们开始讨论多线程的场景:假设现在有线程A和线程B,当两个线程同时来访问Singleton对象,但是在访问期间会有以下不安全的情况:

1)A /B 线程同时访问,这时两个线程都发现singleton为空,所以两个线程都会创建一个singleton变量,这自然不符合单例模式的初衷……

2)在不同的时间,A、B线程分别来访问这个Singleton对象,可能会出现报错的情况:

            单例模式双重校验的再理解_第1张图片

     如上图所示,线程B在T4时间的访问一定会出现NullPointerException,因为找不到这个对象噻!

     关于volatile修饰之后,为什么就可以避免上图中多线程访问的问题,将在下篇中讲解,敬请期待!       

                     单例模式双重校验的再理解_第2张图片   


你可能感兴趣的:(♣【Java菜鸟成长日记】,----,Java基础)