饿汉式单例模式与懒汉式单例模式

       了解单例设计模式的人都知道,单例中涉及的类他在内存之中始终是独一份存在的,如果存在两份则将出现问题,并且单例模式有两种相对比较有特点的形式,那就是饿汉式与懒 汉式单例模式,在本节中我们将会详细讲解单例设计模式的两种形式,并且我们将会讲解如 何在多线程的情况下使用单例设计模式;

      1. 饿汉式单例模式

所谓饿汉式单例设计模式,就是将类的静态实例作为该类的一个成员变量,也就是说在 JVM 加载它的时候就已经创建了该类的实例,因此它不会存在多线程的安全问题,详细代码 请看如下:

public class SingleTest {

private final static SingleTest instance = new SingleTest();

private SingleTest(){

}
public static SingleTest newInstance(){
return instance;
}
}

        可以看到上述代码中的单例不存在线程安全的问题,但是他有一个性能上面的问题,那 就是提前对实例进行了初始化或者说构造,假设构造该类需要很多的性能消耗,如果代码写 成这个样子将会提前完成构造,又假设我们在系统运行过程中压根就没有对该实例进行使用, 那岂不是很浪费系统的资源呢?因此单例设计模式其实还有下一个小节的版本;

  2 懒汉式单例模式

所谓懒汉式单例模式的意思就是,实例虽然作为该类的一个实例变量,但是他不主动进 行创建,如果你不使用它那么他将会永远不被创建,只有你在第一次使用它的时候才会被创 建,并且得到保持;请看下一段代码

public class SingleTest2 {

    private static SingleTest2 instance = null;

    private SingleTest2() {
    }

    public static SingleTest2 newInstance() {
        if (null == instance) {
            instance = new SingleTest2();
        }
        return instance;
    }

}

上述的代码就是我们所说的懒汉式单例模式,但是根据上文中的关于线程安全问题的分 析我们不难发出现,instance 有可能会被创建两次,至于原因为什么呢?让我们根据之前的 IO Programming 系列丛书 由于个人能力有限,书中难免会有偏颇之处,希望读到这本书的朋友能够给予我指导和批评,欢迎你们 的指正 知识点来慢慢分析,然后总结出来一个最优的懒汉式单例模式

 根据我们之前的知识点,运行中的程序很有可能会出现上图所出现的情况。也就是 A 线程和 B 线程有可能同时执行到了 null==instance 的部分他们判断到了 instance 没有被创建, 因此分别实例化了一个以上的 instance,这样的单例类将是非常危险的,那么我们应该如何 避免多线程引起的问题呢,看到这里您可能想到了用 synchronized 这个关键字来解决问题, 于是有了如下的代码

public synchronized static SingleTest newInstance()
{
if(null==instance)
{
instance = new SingleTest();
}
return instance;
}

但是该方法的效率将是相当低下的,因为每一次调用都要获取锁,判断锁的状态,因此 就会出现解决了安全问题,带来了效率问题,当然安全问题和效率问题本来就是两个很不可 调和的矛盾,但是我们也不应该就此妥协,需要尽我们的智慧既解决了安全问题又带来了最 小的效率影响;我们将程序写成如下的样子

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

好的,让我们继续以图示的方式来说明一下,上述代码带来了哪些改变又如何将效率的 损耗降到了最低

通过上述代码的分析,我们不难发现,锁的等待或者争抢最多发生两次,也就是同步代 码块中的代码最多被执行两次,如此一来,安全问题解决了,效率问题也被解决掉了。

3,指令重排优

volatile关键字另一个作用就是禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象,关于指令重排优化参考文章 https://www.cnblogs.com/yuluoxingkong/p/9236077.html

public class SingleTest2 {
    //以关键字volatile修饰之后,就会阻止JVM对其相关代码进行指令重排,这样就能够按照既定的顺序指执行。
    //参考文档 <p>https://www.cnblogs.com/yuluoxingkong/p/9288477.html</p>
    private volatile static SingleTest2 instance = null;

    private SingleTest2() {
    }

    public static SingleTest2 newInstance() {
        if (null == instance) {
            synchronized (SingleTest2.class) {
                if (null == instance) {
                    instance = new SingleTest2(); //非原子操作,可能会出现指令重排优
                }
            }
        }
        return instance;
    }

}
原文地址:https://www.cnblogs.com/yuluoxingkong/p/9802418.html