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

        提起单例模式,作为攻城狮的你我都不会感觉到陌生,而为了确保在程序中的线程安全,我们常常会倾向于双重校验和静态类两种方式。而且众所周知,在双重校验的方式中,我们发现了关键字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对象,可能会出现报错的情况:

            

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

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

                        


评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值