单例设计模式双判空实现以及使用volatile修饰instance讲解

单例设计模式的写法有那么几种,懒汉式和饿汉式,但是相比较而言都不够严谨,存在逻辑漏洞,某些情况下并不能保证完全实现单例,尤其是在并发的情况下,会出现线程不安全的问题,这一点我们这里并不细讲,大家可以自行查找其他文章。

所以双判空加锁的单例写法就出现了,来看看一般人的写法:

public class Test {
    private static Test instance;

    private Test() {

    }

    public static Test getInstance() {
        if (instance == null) {
            synchronized (Test.class) {
                if (instance == null) {
                    instance = new Test();
                }
            }
        }
        return instance;
    }
}

乍一看,老铁没毛病,你还想双击666是不?

我们先来分析一下这段代码:

(1)构造函数使用private修饰,保证了其他类调用的时候不能通过这种方式初始化获得对象。

(2)getInstance方法中第一个判空条件,理论上是可以去除的,那为什么要加上呢?因为去掉了以后,不管有没有给instance赋值,都会进行synchronized加锁操作,但是synchronized操作会耗性能,所以第一次instance赋值后判空,避免了每次synchronized操作,提升性能。

(3)synchronized操作是为了避免多线程并发时出现instance多次赋值而达不到单例的效果,加同步后可以避免这个问题。

(4)第二个判空条件就不能去掉了,因为如果线程a和b同时操作,a先获得锁进入了,判空进入了代码,这个时候如果释放了锁b进入了判空条件先一步进行了初始化工作,这个时候a也进行了初始化工作,这就有了两个实例了,不符合要求,所以第二个判空条件不能去掉哦!

好,现在来说一下这种写法的问题:

问题主要在于instance = new Singleton()这句话并不是原子操作,原子性:即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

instance = new Singleton()其实做了三件事情:

(1)给instance实例分配内存;

(2)初始化Singleton()成员变量;

(3)将instance对象指向new Singleton()分配的内存空间,所以这个时候instance就不为null了;

问题就出在这儿了,因为JVM中有指令重排序的优化,所以呢正常情况按照1,2,3的顺序来,没毛病,但是也可能按照1,3,2的顺序来,这个时候就有问题了,调用的时候判断instance != null就直接返回instance实例,但是这个时候并没有进行初始化工作,所以在后续的调用中肯定就会报错了,所以这里引入了volatile修饰符修饰instance对象,因为volatile能够禁止指令重排序的功能,所以能解决我们的这个问题,最后,写出完整+正确的单例双判空写法如下:

public class Test {
    private volatile static Test instance;

    private Test() {

    }

    public static Test getInstance() {
        if (instance == null) {
            synchronized (Test.class) {
                if (instance == null) {
                    instance = new Test();
                }
            }
        }
        return instance;
    }
}

其实如果说写到这儿,正常是没什么问题的,但是如果说非要挑刺儿的话呢,那就是我们被private修饰的构造函数并不安全,依然可以通过反射的方式创建一个新的对象,详情请跳转
《Java使用反射创建被private修饰的构造函数对象》
查看,感谢!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖子爱你520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值