提起单例模式,作为攻城狮的你我都不会感觉到陌生,而为了确保在程序中的线程安全,我们常常会倾向于双重校验和静态类两种方式。而且众所周知,在双重校验的方式中,我们发现了关键字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对象,可能会出现报错的情况:
![image.png](https://img.haomeiwen.com/i4587933/bb1b30c485451510.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如上图所示,线程B在T4时间的访问一定会出现NullPointerException,因为找不到这个对象噻!
关于volatile修饰之后,为什么就可以避免上图中多线程访问的问题,将在下篇中讲解,敬请期待!
————————————————
版权声明:本文为CSDN博主「杨士超」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/YSC1123/article/details/77867138
网友评论