单例模式double check的演进及原理

单例单例,就是只允许实例化一个对象。一般实现方式也就是将构造方法私有化,然后对外暴露一个获取实例的接口

单例 可以说源自于设计模式中的单例模式吧,多种实现演进,变得越来越靠谱

 

最早单例模式分为懒汉式 饿汉式  

懒汉式:

懒汉式很简单啊,就是全局变量声明时候直接new了,但是这样会有个占用内存的问题,因为如果这个实例用不到,那不是白白浪费空间了,尤其是项目庞大寸土寸金的JVM内存空间

懒汉式的有点也很明显,简单好用,而且不会有乱七八糟的线程安全问题,所以单例的这个实例一定会用到的话 用懒汉式直接实现也无妨

 

饿汉式:

最原始的饿汉式,外部获取实例的时候先判断 实例是不是null,如果空的直接new一个

单线程的世界里这样搞绝对没问题,但是问题就出在了多线程的情况下,当多个线程同时调用getInstance() ,然后同时判断了singlon为null,也就会new两个实例出来,违反了单例。

 

怎么办呢,为了多线程安全 那就加锁呗

这下放心了,多线程来调用这个方法的时候,不许抢都得给我排队串行来执行,也就保证了只会new一次Singlon

但是还有问题,synchronized 这么重量级的锁 直接加到了这个方法上是不是不太好啊,如果多个线程经常频繁的会调用这个方法,都串行执行那也太难受了 (即时jdk1.8 后synchronized会有锁升级)

so 尽量轻量一点 只在if条件确定了singlon==null的时候 再加锁行不行?

if(singlon == null){

  synchronized(this){

    singlon = new Singlon();

  }

}

不行啊,如果恰好就有两个同时判断了singlon == null 然后同时进入到了if的代码块,加锁并没有阻止第二个线程new Singlon啊

 

应运而生了double-check    一次判断不够用 我再来一次被

在锁里在判断一次 这下放心了吧

 

 

以为到这万事大吉了,其实并不然.虽然这种情况发生的概率不大,指令重排序导致的多线程安全问题

singlon = new Singlon();  这一行代码 在底层其实是三条指令完成的

1.开辟一块堆内存空间 

2. 初始化一个Singlon对象

3. singlon声明指向这个对象

 

由于指令重排,很有可能 2 3步进行重排,1 3 2 的顺序执行,

但第一个线程执行完了1 3步,这个时候,第二个线程进入方法 判断了一下singlon不为null 就直接返回了,事实上这个 singlon表面上不为null 但是还没来得及实实在在的实例化,导致直接return出问题

那咋办呢  加volatile 防止指令重排呗

 

 

实现单例的方式还有很多  比如常用的静态内部类 静态代码块 或者更简单直接用枚举它不香吗? 这里就不一一列举了 over

 

posted @ 2020-12-29 20:28  六小扛把子  阅读(480)  评论(0编辑  收藏  举报